Compare commits

...
99 Commits
Author SHA1 Message Date
Nubenetes Bot 0ae118d85b release: v2.9.0 — fix publisher safety: no pruning in render-only, V2 sanity check in deploy 2026-06-19 13:29:43 +02:00
Inaki 9c4adeb5ac Merge pull request #371 from nubenetes/feat/fix-publisher-safety
fix: 3 critical publisher/deploy safety issues
2026-06-19 13:29:28 +02:00
Nubenetes BotandClaude Sonnet 4.6 1dd0dab308 fix: 3 critical publisher/deploy safety issues
1. Skip page deletion and nav sync in --render-only mode
   The CI publisher always uses --render-only but the pruning phase was
   deleting pages not regenerated in a given pass (e.g. low-hit pages),
   breaking nav references and corrupting the MkDocs build.

2. Protect dimension pages from deletion in full mode
   Even in a full (non-render-only) run, pages defined in self.dimensions
   are never deleted. Truly orphaned pages (not in dimensions AND not
   generated) are the only ones pruned.

3. _sync_enterprise_navigation returns True/False
   Deletion is gated on nav sync success. If nav sync fails, deletion
   is skipped to prevent inconsistency (deleted files + stale nav).
   Also fixes the fragile re.sub(r'nav:.*') regex by using string
   indexing instead, preventing accidental truncation of extra_css etc.

4. Deploy workflow V2 sanity check
   If V2 build produces fewer than 50 HTML pages or no index.html,
   deploy falls back to V1-only instead of overwriting V1 with a
   broken V2 build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:29:10 +02:00
Inaki e599793928 Merge pull request #370 from nubenetes/rollback/v2.8.4-restore
HOTFIX: revert to v2.8.4 — publisher deletions broke V2 portal
2026-06-19 13:20:55 +02:00
nubenetes-bot 7f5fbe34ee style(cleaner): auto-correcting formatting & URL normalization 2026-06-19 11:20:45 +00:00
Nubenetes BotandClaude Sonnet 4.6 119bd4158f revert: restore v2.8.4 state — publisher deletions broke V2 portal
The publisher deleted ~30 v2-docs pages still referenced in v2-mkdocs.yml nav,
producing a broken MkDocs build. Rolling back to last known good state (v2.8.4).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:19:40 +02:00
Nubenetes Bot a355889969 docs: automated README metric synchronization [skip ci] 2026-06-19 11:07:02 +00:00
Nubenetes Bot cf717c67ce release: v2.8.5 — fix invalid extra_head and broken redirect in v2-mkdocs.yml 2026-06-19 13:07:00 +02:00
Inaki 512b37287e Merge pull request #369 from nubenetes/feat/fix-mkdocs-config
fix: remove invalid extra_head + fix broken redirect in v2-mkdocs.yml
2026-06-19 13:06:39 +02:00
Nubenetes BotandClaude Sonnet 4.6 23256352a7 fix: remove invalid extra_head key and fix broken redirect target
- Remove extra_head (not a valid MkDocs config key, was breaking V2 build)
- Fix digital-money.md redirect: finops.md was deleted by publisher,
  redirect now points to kubernetes.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:06:30 +02:00
Inaki 101b341200 Merge pull request #368 from nubenetes/develop
🚀 Release: Agentic V2 Portal Update
2026-06-19 12:53:51 +02:00
Nubenetes Bot 1b56b77cbf feat: sync V2 elite curated edition and README metrics [skip ci] 2026-06-19 10:51:23 +00:00
Nubenetes Bot 655ad7d5ed docs: automated README metric synchronization [skip ci] 2026-06-19 10:45:46 +00:00
Nubenetes Bot 29e2593e42 merge: back-merge v2.8.4 into develop 2026-06-19 12:45:43 +02:00
Nubenetes Bot 1e89f8af00 release: v2.8.4 — comprehensive None-stars guard in v2_optimizer 2026-06-19 12:45:39 +02:00
Inaki 1a092a5af2 Merge pull request #367 from nubenetes/feat/fix-stars-none-comprehensive
fix: comprehensive None-stars guard in _render_single_link and all comparisons
2026-06-19 12:45:26 +02:00
Nubenetes BotandClaude Sonnet 4.6 f4208bca4d fix: comprehensive None-stars guard across all v2_optimizer comparisons
_render_single_link (line 872) crashed with TypeError: '>=' not supported
between NoneType and int. Replaced ALL remaining .get('stars', 0) patterns
with .get('stars') or 0 throughout v2_optimizer.py to prevent further
crashes from null stars values in inventory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:45:20 +02:00
Nubenetes Bot 90d04e6d6a merge: back-merge v2.8.3 into develop 2026-06-19 12:14:57 +02:00
Nubenetes Bot 7d845fab3e release: v2.8.3 — cache news_digest.json to skip Gemini on same-day re-runs 2026-06-19 12:14:53 +02:00
Inaki fbfa85c2d3 Merge pull request #366 from nubenetes/feat/digest-cache
feat: cache news_digest.json by date, skip Gemini on same-day re-runs
2026-06-19 12:14:41 +02:00
Nubenetes BotandClaude Sonnet 4.6 dc4591fb3e feat: cache news_digest.json by date to avoid re-calling Gemini on re-runs
Publisher and weekly digest workflows now restore news_digest.json from
GitHub Actions cache using key news-digest-YYYY-MM-DD. If cache hits
(same-day re-run after publisher failure), the Gemini digest step is
skipped entirely — saving ~20min and Gemini API credits.

Cache is saved immediately after successful generation and expires
automatically after 7 days (GitHub Actions cache policy).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:14:34 +02:00
Nubenetes Bot 96706aff90 docs: automated README metric synchronization [skip ci] 2026-06-19 10:13:08 +00:00
Nubenetes Bot f3cb3a1ff3 merge: back-merge v2.8.2 into develop 2026-06-19 12:13:04 +02:00
Nubenetes Bot cc6633dcdd release: v2.8.2 — fix all None-stars sort crashes in v2_optimizer 2026-06-19 12:12:58 +02:00
Inaki 759c10ad2c Merge pull request #365 from nubenetes/feat/fix-stars-none-sort
fix: all negated .get(stars) sort keys crash on null values
2026-06-19 12:12:46 +02:00
Nubenetes BotandClaude Sonnet 4.6 58601de338 fix: guard all negated .get('stars') sort keys against None values
Three sites in v2_optimizer where `-x.get("stars", default)` crashes
with TypeError when stars field is present but null in inventory.
Fixed with `-(x.get("stars") or default)` pattern consistently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:12:39 +02:00
Nubenetes Bot 11ebda6187 docs: automated README metric synchronization [skip ci] 2026-06-19 09:43:30 +00:00
Nubenetes Bot 227dc23933 merge: back-merge v2.8.1 into develop 2026-06-19 11:43:22 +02:00
Nubenetes Bot 56db4d5e53 release: v2.8.1 — fix None resource_type crash in v2_optimizer 2026-06-19 11:43:18 +02:00
Inaki e332af2ea8 Merge pull request #364 from nubenetes/feat/fix-resource-type-none
fix: None resource_type crash in _calculate_tags
2026-06-19 11:43:05 +02:00
Nubenetes BotandClaude Sonnet 4.6 9d3321d0f0 fix: guard against None resource_type in _calculate_tags
item.get("resource_type", "Reference") returns None when the field
exists with a null value in the inventory. Use `or "Reference"` instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:42:58 +02:00
Nubenetes Bot 9ed1318a9c merge: back-merge v2.8.0 into develop 2026-06-19 11:12:21 +02:00
Nubenetes Bot 91da9c8495 release: v2.8.0 — geo inference, last-updated badge, search boost, RSS feed, weekly cron 2026-06-19 11:12:16 +02:00
Nubenetes Bot c9a7b2a8cb docs: automated README metric synchronization [skip ci] 2026-06-19 09:11:47 +00:00
Inaki ad59cb75f0 Merge pull request #363 from nubenetes/feat/five-improvements
feat: geo inference, last-updated badge, search boost, RSS feed, weekly cron
2026-06-19 11:11:32 +02:00
Nubenetes BotandClaude Sonnet 4.6 88f891fd4e feat: 5 improvements — geo inference, last-updated badge, search boost, RSS feed, weekly cron
1. geo_region inference: _infer_geo_from_url() infers Americas/Europe/España/Asia-Pacific
   from URL TLD (.es .de .fr .uk .jp .cn etc.) as fallback when geo_region field is empty.
   Industry digest now shows real content instead of empty categories.

2. Last-updated badge: trending section header now shows "Updated Jun 19, 2026" pill
   derived from news_digest.json mtime — gives readers confidence in freshness.

3. Search boost: tech-digest and industry-digest pages now have search.boost: 2
   frontmatter so they rank higher in MkDocs Material site search.

4. RSS feed (src/rss_generator.py): generates v2-docs/feed.xml with top-20 curated
   picks from 3-month digest. Runs after news_digest in publisher. Autodiscovery
   <link rel="alternate"> added to v2-mkdocs.yml extra_head.

5. Weekly cron (09.weekly_digest.yml): runs every Monday 06:00 UTC, generates digest
   with Gemini, renders digest pages, commits, then triggers publisher.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:11:02 +02:00
Nubenetes Bot 61733937be merge: back-merge v2.7.1 into develop 2026-06-19 11:06:42 +02:00
Nubenetes Bot 10c70025a2 release: v2.7.1 — remove redundant digest-preview list from index 2026-06-19 11:06:38 +02:00
Inaki cff4903166 Merge pull request #362 from nubenetes/feat/remove-digest-preview-from-index
refactor: remove digest-preview list from index
2026-06-19 11:05:54 +02:00
Nubenetes BotandClaude Sonnet 4.6 16ad26f427 refactor: remove digest-preview list from index, keep trending cards only
The 5-item link list was redundant with the 6 trending cards block
that the publisher generates. Cleaner UX: hero card (amber) → trending
cards → /tech-digest/ for full 22-category view.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:05:41 +02:00
Nubenetes Bot 3f3a86b62c merge: back-merge master v2.7.0 into develop 2026-06-19 10:59:38 +02:00
Nubenetes Bot 239dd05e6f release: v2.7.0 — Intelligence Digest card and mini-preview on index 2026-06-19 10:59:31 +02:00
Inaki 932770b89b Merge pull request #361 from nubenetes/feat/digest-card-index
feat: Intelligence Digest hero card and mini-preview on index
2026-06-19 10:59:15 +02:00
Nubenetes BotandClaude Sonnet 4.6 c8ca878f66 feat: add Intelligence Digest card to index hero and mini-preview block
- New hero card (amber color) between AI & MCP Agents and Agentic Video
  Hub, linking to /tech-digest/ with emoji icon and subtitle
- Dynamic digest-preview block above Agentic Pulse: shows top 1 link
  from 5 priority categories (Kubernetes, AI, Security, IaC, Observability)
  pulled from data/news_digest.json on each optimizer run
- New CSS: hero-badge-card--amber, hero-badge-icon, digest-preview
  component with category chips and hover states
- v2_optimizer now generates both the card and preview automatically

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:58:52 +02:00
Nubenetes Bot 64c6e9ea18 Merge branch 'develop' of github.com:nubenetes/awesome-kubernetes into develop 2026-06-19 10:44:46 +02:00
Nubenetes Bot 53b19f1b09 merge: back-merge master v2.6.9 into develop 2026-06-19 10:44:36 +02:00
Nubenetes Bot f6e60a6487 docs: automated README metric synchronization [skip ci] 2026-06-19 08:44:35 +00:00
Nubenetes Bot 5bfcaf1b03 release: v2.6.9 — Fix NoneType crashes in v2_optimizer, dedup, and news_digest 2026-06-19 10:44:32 +02:00
Inaki b8144d9253 Merge pull request #360 from nubenetes/feat/fix-nonetype-comparisons
fix: NoneType crashes in v2_optimizer, dedup, and news_digest
2026-06-19 10:44:13 +02:00
Nubenetes BotandClaude Sonnet 4.6 b321169459 fix: NoneType comparison crashes in v2_optimizer, dedup, and news_digest
Inventory entries can have stars=null or discovered_at=null. Replace
.get("stars", 0) with .get("stars") or 0 in all three modules so that
None values are safely coerced to 0/"" before numeric/string comparison.

Fixes TypeError crashes in:
- v2_optimizer.py:632 _calculate_tags
- dedup.py:74,108 title dedup and entry scoring
- news_digest.py:322 category pool sorting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:44:00 +02:00
Nubenetes Bot 34610f4671 merge: back-merge master v2.6.8 into develop 2026-06-19 10:37:06 +02:00
Nubenetes Bot f50fdffd64 release: v2.6.8 — Fix unbounded license check causing CI timeout 2026-06-19 10:37:01 +02:00
Inaki f5fae99a38 Merge pull request #359 from nubenetes/feat/fix-license-check-limit
fix: cap license check at 200 repos — was unbounded causing CI timeout
2026-06-19 10:36:46 +02:00
Nubenetes BotandClaude Sonnet 4.6 ebae3c6e27 fix: cap license change detection at MAX_REPOS_DEFAULT (200) — was unbounded
detect_license_changes() iterated all repos with gh_license set, causing
hundreds/thousands of GitHub API calls. Apply the same 200-repo cap as
the activity enrichment module to keep total enrichment under ~3 min.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:36:33 +02:00
Nubenetes Bot a8941afd24 merge: back-merge master v2.6.7 into develop 2026-06-19 10:28:44 +02:00
Nubenetes Bot f4fb5fe19b release: v2.6.7 — Reduce enrichment CI time from 12min to ~2min 2026-06-19 10:28:39 +02:00
Inaki f46886abf3 Merge pull request #358 from nubenetes/feat/fix-enrichment-speed
perf: reduce enrichment from 12min to ~2min
2026-06-19 10:28:22 +02:00
Nubenetes BotandClaude Sonnet 4.6 6ccda021e2 perf: reduce enrichment from 12min to ~2min (1 API call/repo, 200 limit)
- Consolidate 2 GitHub API calls per repo into 1: open_issues_count
  already includes PRs on GitHub, second /pulls call was redundant
- Reduce MAX_REPOS_DEFAULT 500→200: sufficient for meaningful enrichment,
  200 × 0.5s = ~100s vs 500 × 1.5s = ~12.5min in CI
- Reduce GITHUB_RATE_DELAY 0.75s→0.5s: still safely under 5000 req/hr
- Add 429 rate-limit backoff (5s sleep instead of crashing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:28:07 +02:00
Nubenetes Bot 3f44948119 merge: back-merge master v2.6.6 into develop 2026-06-19 10:16:41 +02:00
Nubenetes Bot fe024b4fd7 release: v2.6.6 — Fix CNCF landscape API (SPA→GitHub topic search) 2026-06-19 10:16:37 +02:00
Inaki bdcedd6950 Merge pull request #357 from nubenetes/feat/fix-cncf-api
fix: CNCF landscape API now SPA — use GitHub topic search instead
2026-06-19 10:16:20 +02:00
Nubenetes BotandClaude Sonnet 4.6 6769fff528 fix: replace CNCF landscape SPA endpoint with GitHub topic search
The legacy landscape.cncf.io/api/items endpoint now returns HTML (SPA)
instead of JSON. Switch to GitHub Search API querying cncf-graduated,
cncf-incubating, and cncf-sandbox topics — reliable and uses existing
GH_TOKEN auth.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:16:07 +02:00
Nubenetes Bot 5b00ed4824 merge: back-merge master v2.6.5 into develop 2026-06-19 09:51:32 +02:00
Nubenetes Bot 7e85ec6bdd release: v2.6.5 — Fix MD023 linter errors in digest pages 2026-06-19 09:51:27 +02:00
Inaki dc0e090318 Merge pull request #356 from nubenetes/feat/fix-digest-linter
fix: MD023 lint — replace indented headings with bold in digest tabs
2026-06-19 09:51:08 +02:00
Nubenetes BotandClaude Sonnet 4.6 6f34ac8569 fix: replace indented ## headings with bold text in digest pages (MD023 lint fix)
- Change `    ## Category` to `    **Category**` inside MkDocs Material
  tabs to avoid MD023 linter errors (headings must start at col 0)
- Sanitize pipe characters in "why" text to prevent broken markdown tables
- Regenerated tech-digest.md (1328 lines, 3 tabs: 333/443/552 lines)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:50:53 +02:00
Nubenetes Bot 5d7f58c7fa merge: back-merge master v2.6.4 into develop 2026-06-19 09:45:22 +02:00
Nubenetes Bot 52e2560fa0 release: v2.6.4 — Fix digest periods differentiation 2026-06-19 09:45:18 +02:00
Inaki 41207ca0f0 Merge pull request #355 from nubenetes/feat/fix-digest-periods
fix: differentiate digest periods and fix NoneType stars
2026-06-19 09:44:59 +02:00
Nubenetes BotandClaude Sonnet 4.6 b5b3dc3ce2 fix: differentiate digest periods (10/15/20 items), fix year fallback, fix NoneType stars
- Variable items per period: 3 months=10, 6 months=15, 12 months=20
  so each tab shows progressively more content
- Use year field as fallback in _is_within_period when discovered_at
  doesn't discriminate (backfilled entries)
- Fix NoneType comparison in _fallback_items for entries with null stars
- Regenerated digest: 3m=220, 6m=330, 12m=440 items across 22 categories
- tech-digest.md now 1353 lines with differentiated tabs (339/449/559 lines)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:44:44 +02:00
Nubenetes Bot f221647646 merge: back-merge master v2.6.3 into develop 2026-06-19 09:33:18 +02:00
Nubenetes Bot 099a40d6b1 release: v2.6.3 — First Intelligence Digest with real content 2026-06-19 09:33:13 +02:00
Inaki 74cbc47e2e Merge pull request #354 from nubenetes/feat/generate-digest-content
feat: first Intelligence Digest with real content (22 categories)
2026-06-19 09:32:38 +02:00
Nubenetes BotandClaude Sonnet 4.6 f90d66478f feat: generate first Intelligence Digest with real content (22 categories, 660 items)
- Generate news_digest.json from inventory using star-based ranking
  (no Gemini tokens consumed — fallback mode)
- tech-digest.md: 1022 lines with 22 categories across 3/6/12 month tabs
- industry-digest.md: placeholder (geo_region data will populate after
  next curator run with updated Gemini prompts)
- Add gitignore exception for data/news_digest.json
- 220 items per time period across Kubernetes, AI, DevOps, Security,
  IaC, Observability, Cloud Providers, and more

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:32:23 +02:00
Nubenetes Bot d846c6f336 merge: back-merge master v2.6.2 into develop 2026-06-19 09:29:29 +02:00
Nubenetes Bot 01634bceb2 release: v2.6.2 — Fix .html URLs, add digest placeholders, clean URL mandate 2026-06-19 09:29:24 +02:00
Inaki d456128366 Merge pull request #353 from nubenetes/feat/fix-html-urls-and-digest-placeholders
fix: remove offline plugin, add digest placeholders, enforce clean URL mandate
2026-06-19 09:29:04 +02:00
Nubenetes BotandClaude Sonnet 4.6 13d51e8fa9 fix: remove offline plugin (.html suffix), add digest placeholder pages, enforce clean URL mandate
- Remove offline plugin from v2-mkdocs.yml — it forces .html suffixes
  on all URLs, breaking SEO and existing deep-links
- Add placeholder tech-digest.md and industry-digest.md pages to prevent
  404 errors before first Gemini digest generation run
- Add clean URL mandate to CLAUDE.md and GEMINI.md: offline plugin is
  permanently forbidden, use_directory_urls must always be true
- Update README with V2 URL Policy section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:28:50 +02:00
Nubenetes Bot 87809870c5 merge: back-merge master v2.6.1 into develop 2026-06-19 09:25:40 +02:00
Nubenetes Bot e1b49ab4b5 release: v2.6.1 — Add CLAUDE.md and gitflow mandate 2026-06-19 09:25:35 +02:00
Inaki 57f9c618f1 Merge pull request #352 from nubenetes/feat/gitflow-docs
docs: add CLAUDE.md and gitflow mandate to GEMINI.md
2026-06-19 09:25:21 +02:00
Nubenetes BotandClaude Sonnet 4.6 65ce209183 docs: add CLAUDE.md with gitflow instructions and update GEMINI.md with git workflow mandate
- Create CLAUDE.md documenting gitflow branching model, release process,
  repository structure, build commands, and coding conventions for Claude
  Code agents
- Add Gitflow section to GEMINI.md (Mandate) ensuring Gemini agents also
  follow the branching discipline: feat→develop→release→master+tag+release
- Both files ensure all AI agents follow gitflow without manual reminders

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:24:56 +02:00
Nubenetes Bot fdd9eac809 merge: back-merge master v2.6.0 into develop 2026-06-19 09:23:03 +02:00
Nubenetes Bot 059b4de3e8 release: v2.6.0 — Intelligence Digest, Pipeline Hardening & Portal Overhaul 2026-06-19 09:22:12 +02:00
Inaki a11bfe4ced Merge pull request #351 from nubenetes/feat/v2-news-digest-and-improvements
feat: V2 Intelligence Digest, Pipeline Hardening & Portal Overhaul
2026-06-19 09:21:22 +02:00
Nubenetes BotandClaude Sonnet 4.6 9cb73cadcc docs: update README with Intelligence Digest, enrichment pipeline, dedup engine, and MkDocs enhancements
- Add V2 Intelligence Digest section documenting 26-category temporal
  digest system with 3/6/12 month panels
- Add V2 Data Quality and Pipeline Hardening section documenting CNCF
  integration, GitHub activity enrichment, license detection, dedup,
  exception observability, expanded discovery, and stale health re-check
- Add V2 MkDocs Material Enhancements section documenting instant nav,
  breadcrumbs, announcement bar, tags/RSS/PWA plugins, and stub merges
- Update Agentic Stack table with News Digest, Enrichment, Dedup, and
  PWA capabilities
- Update Repository Inventory with new source code modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:19:47 +02:00
Nubenetes BotandClaude Sonnet 4.6 05b66d006f feat: add company/geo_region extraction to Gemini prompts, add enrichment/dedup to cron, fix nav sync for digest pages
- Update curator Gemini prompt with Phase 5: Company & Geo Classification
  to extract company name and geo_region (americas/europe/spain/asia_pacific)
  for industry digest categories
- Store company and geo_region from AI response in eval_data
- Add Intelligence Digest nav entries to _sync_enterprise_navigation so
  digest pages persist across nav rebuilds
- Add dedup scan and enrichment pipeline steps to monthly cron workflow
  (01.1.agentic_cron.yml) in addition to publish workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:04:28 +02:00
Nubenetes BotandClaude Sonnet 4.6 3dc52af391 feat: add CNCF landscape integration, GitHub activity enrichment, license change detection, dedup engine, PWA support, and expanded video hub
- Add src/enrichment.py (395 lines): CNCF landscape API integration for
  project graduation status, GitHub issue/PR velocity tracking with
  community health scoring, and license change detection with history
- Add src/dedup.py (194 lines): URL normalization dedup, content-hash
  dedup, title-similarity dedup (85% threshold via SequenceMatcher),
  and resolution strategy keeping highest-quality entry
- Enable PWA/offline plugin in v2-mkdocs.yml for cached offline reading
- Expand video hub categorization with MLOps, security, IaC, GitOps,
  observability, FinOps, cloud providers, and language-specific routing
- Add dedup scan and enrichment pipeline steps to CI publish workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 09:00:53 +02:00
Nubenetes BotandClaude Sonnet 4.6 8b04851097 fix: exclude digest pages from v2_filter.js injection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:46:47 +02:00
Nubenetes BotandClaude Sonnet 4.6 782dc1bd06 feat: add AI re-evaluation staleness detection, cross-dimension See Also, and last_ai_eval tracking
- Add last_ai_eval timestamp to eval_data in both fast-track and grounded-track
  evaluation paths, plus SQL schema and curator
- Entries enriched >6 months ago are automatically flagged for re-evaluation
  instead of being skipped (stale content detection)
- Enhance "See Also" links with cross-dimension references based on shared
  tags between pages, in addition to same-dimension related links
- Add _collect_tags_from_tree() helper for recursive tag extraction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:45:57 +02:00
Nubenetes BotandClaude Sonnet 4.6 1149a9c8f7 feat: add announcement bar, tags/RSS/minify plugins, markdown extensions, and fix comparison table threshold
- Add announcement bar with digest promo via template override
- Enable tags plugin (native clickable tag navigation)
- Add RSS plugin for digest feed subscription
- Enable minify plugin for production HTML optimization
- Add markdown extensions: highlight, inlinehilite, smartsymbols, caret, tilde, tables, footnotes, abbr, def_list
- Add mkdocs-rss-plugin to requirements.txt
- Fix comparison table threshold mismatch (config: 8, code: 5 → aligned to 8)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:42:17 +02:00
Nubenetes BotandClaude Sonnet 4.6 4e87c248fd feat: implement AI-powered news digest engine, MkDocs UX overhaul, pipeline hardening, and stub page merges
- Add 26-category news digest engine (src/news_digest.py) with Gemini AI ranking
  for 3/6/12 month temporal panels across tech, cloud, and geo categories
- Add discovered_at, company, geo_region fields to inventory schema with backfill
  script populating 18K+ existing entries
- Fix critical v2-mkdocs.yml bug: plugins were nested under theme (silently disabled)
- Add MkDocs Material features: instant nav, breadcrumbs, footer, announce bar
- Add trending cards CSS grid and replace Agentic Pulse with dynamic Trending Now
- Generate tech-digest.md and industry-digest.md with tabbed 3/6/12 month views
- Merge 12 stub pages (<40 lines each) into parent categories with redirects
- Replace 50 bare except:pass patterns with contextual logging across all pipeline files
- Expand autonomous discovery from 6 to 14 GitHub search queries
- Add stale health re-check for online entries older than 30 days
- Track addition_method by source type (rss, twitter, github_trending)
- Add digest generation step to CI publish workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:38:56 +02:00
Nubenetes Bot 51e8ec8cfa docs: automated README metric synchronization [skip ci] 2026-06-18 15:42:37 +00:00
Nubenetes Bot 138b63c69f fix(ci): fix README.md TOC and safety check logic, and guard against NoneType titles in v2_optimizer 2026-06-18 17:42:04 +02:00
Nubenetes Bot 95c7078e6a fix(ci): stage both inventory.yaml and inventory.sql in workflows to prevent unstaged rebase failures 2026-06-18 17:35:40 +02:00
Nubenetes Bot cba67c689c merge branch 'develop' 2026-06-18 17:33:04 +02:00
Nubenetes Bot e527be5189 feat: implement SQLite dual-save engine, pre-commit schema linting, debate consensus caching, and reputation registry 2026-06-18 17:32:48 +02:00
Nubenetes Bot 1cd243ee58 feat: implement SQLite dual-save engine, pre-commit schema linting, debate consensus caching, and reputation registry 2026-06-18 17:32:08 +02:00
Nubenetes Bot 0c3ae9661c docs: automated README metric synchronization [skip ci] 2026-06-18 15:04:18 +00:00
77 changed files with 315312 additions and 169756 deletions
+15
View File
@@ -178,6 +178,21 @@ jobs:
gh workflow run 01.1.agentic_cron.yml -f historical_mode=true -f historical_chunked=true -f historical_until_date=$NEXT_DATE
fi
- name: Run Deduplication Scan
if: success()
env:
PYTHONPATH: .
run: |
python -u -c "import asyncio; from src.dedup import run_dedup; asyncio.run(run_dedup(dry_run=False))" || echo "Dedup scan skipped"
- name: Run Enrichment Pipeline
if: success()
env:
PYTHONPATH: .
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python -u -m src.enrichment || echo "Enrichment pipeline skipped"
- name: Upload Visual Dashboard Artifact
if: always()
uses: actions/upload-artifact@v7
@@ -61,9 +61,9 @@ jobs:
run: |
git config --global user.name "Nubenetes Bot"
git config --global user.email "bot@nubenetes.com"
git add data/inventory.yaml
git add data/inventory.yaml data/inventory.sql
if git diff --staged --quiet; then
echo "No changes in inventory.yaml to commit."
echo "No changes in inventory to commit."
else
git commit -m "chore: update inventory stars and licenses [skip ci]"
git pull origin develop --rebase
+1 -1
View File
@@ -75,7 +75,7 @@ jobs:
run: |
git config --global user.name "Nubenetes Bot"
git config --global user.email "bot@nubenetes.com"
git add data/inventory.yaml
git add data/inventory.yaml data/inventory.sql
if git diff --staged --quiet; then
echo "No changes in AI analysis to commit."
else
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
run: |
git config --global user.name "Nubenetes Bot"
git config --global user.email "bot@nubenetes.com"
git add data/inventory.yaml v2-docs/videos/
git add data/inventory.yaml data/inventory.sql v2-docs/videos/
if git diff --staged --quiet; then
echo "No automated changes to commit."
else
+49 -1
View File
@@ -60,7 +60,20 @@ jobs:
- name: Installation of Dependencies
run: |
pip install --no-cache-dir pydantic PyGithub httpx fake-useragent pytz python-dotenv pyyaml tenacity
- name: Get current date for digest cache key
id: digest-date
run: echo "date=$(date -u +%Y-%m-%d)" >> $GITHUB_OUTPUT
- name: Restore News Digest Cache
id: cache-digest
uses: actions/cache/restore@v5
with:
path: data/news_digest.json
key: news-digest-${{ steps.digest-date.outputs.date }}
restore-keys: |
news-digest-
- name: Execute Video Portal Generator
env:
PYTHONPATH: ${{ github.workspace }}
@@ -73,6 +86,41 @@ jobs:
run: |
python src/reorganize_mosaic.py
- name: Run Deduplication Scan
env:
PYTHONPATH: ${{ github.workspace }}
run: |
python -u -c "import asyncio; from src.dedup import run_dedup; asyncio.run(run_dedup(dry_run=False))" || echo "Dedup scan skipped"
- name: Run Enrichment Pipeline
env:
PYTHONPATH: ${{ github.workspace }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python -u -m src.enrichment || echo "Enrichment pipeline skipped (no token or error)"
- name: Generate News Digest
if: steps.cache-digest.outputs.cache-hit != 'true'
env:
PYTHONPATH: ${{ github.workspace }}
GEMINI_API_KEY_1: ${{ secrets.GEMINI_API_KEY_1 }}
GEMINI_API_KEY_2: ${{ secrets.GEMINI_API_KEY_2 }}
run: |
python -u -m src.news_digest || echo "News digest generation skipped (no API key or error)"
- name: Generate RSS Feed
env:
PYTHONPATH: ${{ github.workspace }}
run: |
python -u -m src.rss_generator || echo "RSS generation skipped"
- name: Save News Digest Cache
if: steps.cache-digest.outputs.cache-hit != 'true' && hashFiles('data/news_digest.json') != ''
uses: actions/cache/save@v5
with:
path: data/news_digest.json
key: news-digest-${{ steps.digest-date.outputs.date }}
- name: Run V2 Publisher (Render-Only)
env:
PYTHONPATH: ${{ github.workspace }}
+13
View File
@@ -49,6 +49,19 @@ jobs:
- name: Assemble Final Site
run: |
mkdir -p site/
# Safety check: V2 must have a valid index.html and at least 50 pages
V2_PAGE_COUNT=$(find site_v2/ -name "*.html" | wc -l)
if [ "$V2_PAGE_COUNT" -lt 50 ] || [ ! -f site_v2/index.html ]; then
echo "❌ V2 build looks broken (only $V2_PAGE_COUNT HTML pages). Deploying V1 only to protect the site."
cp -r site_v1/* site/
mkdir -p site/v1/
cp -r site_v1/* site/v1/
rm -rf site_v1/ site_v2/
exit 0
fi
echo "✅ V2 build OK ($V2_PAGE_COUNT pages). Assembling dual-version site."
# Copy V1 to root as fallback for any pages not present in V2
cp -r site_v1/* site/
# Overwrite root with V2 Elite Portal (matching pages will be V2 versions)
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
run: |
git config --global user.name "nubenetes-bot"
git config --global user.email "bot@nubenetes.com"
git add docs/ v2-docs/ README.md data/inventory.yaml src/memory/
git add docs/ v2-docs/ README.md data/inventory.yaml data/inventory.sql src/memory/
if ! git diff --cached --quiet; then
git commit -m "style(cleaner): auto-correcting formatting & URL normalization"
git push origin HEAD:${{ github.event.pull_request.head.ref }} || echo "⚠️ Push failed (likely fork permission limit)."
+96
View File
@@ -0,0 +1,96 @@
name: 09. Weekly Intelligence Digest
on:
schedule:
# Every Monday at 06:00 UTC (08:00 Madrid time)
- cron: '0 6 * * 1'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: develop-git-write-lock
cancel-in-progress: false
jobs:
weekly-digest:
runs-on: ubuntu-latest
steps:
- name: Repository Synchronization
uses: actions/checkout@v6
with:
ref: develop
fetch-depth: 0
- name: Python 3.11 Environment Provisioning
uses: actions/setup-python@v6
with:
python-version: '3.11'
cache: 'pip'
- name: Install Dependencies
run: pip install -r requirements.txt
- name: Get current date for digest cache key
id: digest-date
run: echo "date=$(date -u +%Y-%m-%d)" >> $GITHUB_OUTPUT
- name: Restore News Digest Cache
id: cache-digest
uses: actions/cache/restore@v5
with:
path: data/news_digest.json
key: news-digest-${{ steps.digest-date.outputs.date }}
restore-keys: |
news-digest-
- name: Generate News Digest (Gemini)
if: steps.cache-digest.outputs.cache-hit != 'true'
env:
PYTHONPATH: ${{ github.workspace }}
GEMINI_API_KEY_1: ${{ secrets.GEMINI_API_KEY_1 }}
GEMINI_API_KEY_2: ${{ secrets.GEMINI_API_KEY_2 }}
run: |
python -u -m src.news_digest
- name: Save News Digest Cache
if: steps.cache-digest.outputs.cache-hit != 'true' && hashFiles('data/news_digest.json') != ''
uses: actions/cache/save@v5
with:
path: data/news_digest.json
key: news-digest-${{ steps.digest-date.outputs.date }}
- name: Generate RSS Feed
env:
PYTHONPATH: ${{ github.workspace }}
run: |
python -u -m src.rss_generator || echo "RSS generation skipped"
- name: Render V2 Portal (Digest Pages Only)
env:
PYTHONPATH: ${{ github.workspace }}
PYTHONUNBUFFERED: "1"
run: |
python -u -m src.v2_optimizer --render-only
- name: Commit and Push Weekly Digest
run: |
git config --global user.name "Nubenetes Bot"
git config --global user.email "bot@nubenetes.com"
git add data/news_digest.json v2-docs/tech-digest.md v2-docs/industry-digest.md v2-docs/feed.xml || true
if git diff --staged --quiet; then
echo "No digest changes to commit."
else
git commit -m "feat: weekly intelligence digest update [skip ci]"
for i in {1..3}; do
git pull origin develop --rebase && git push origin develop && break || sleep 10
done
fi
- name: Trigger V2 Publisher
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
event-type: weekly-digest-ready
+1
View File
@@ -351,6 +351,7 @@ MigrationBackup/
# Automatización Nubenetes
src/__pycache__/
*.json
!data/news_digest.json
.env
nubenetes_agent_env/
.venv/
+93
View File
@@ -0,0 +1,93 @@
# CLAUDE.md — Nubenetes Project Instructions for Claude Code
## Git Workflow: Gitflow
This repository uses **Gitflow**. All agents MUST follow this branching model:
### Branches
- **`master`** — Production. Only receives merges from release branches. Every merge to master gets a **tag** and a **GitHub Release**.
- **`develop`** — Integration branch. All feature branches merge here first.
- **`feat/*`** — Feature branches. Created from `develop`, merged back to `develop` via PR.
- **`release/vX.Y.Z`** — Release branches. Created from `develop` when ready to release, merged to both `master` AND back to `develop`.
- **`gh-pages`** — Deployment. Never touch directly.
### Release Process (mandatory for all releases)
1. Create feature branch from `develop`: `git checkout -b feat/description develop`
2. Implement changes, commit, push feature branch
3. Create PR: `gh pr create --base develop --head feat/description`
4. Merge PR to develop: `gh pr merge N --merge`
5. Create release branch: `git checkout -b release/vX.Y.Z develop`
6. Merge release to master: `git checkout master && git merge release/vX.Y.Z --no-ff`
7. Tag: `git tag -a vX.Y.Z -m "description"`
8. Push master + tag: `git push origin master && git push origin vX.Y.Z`
9. Back-merge master to develop: `git checkout develop && git merge master --no-ff && git push origin develop`
10. Create GitHub Release: `gh release create vX.Y.Z --title "..." --notes "..."`
### Versioning
- Current: `v2.6.0`
- Format: `v{major}.{minor}.{patch}`
- Major: breaking changes or architectural shifts
- Minor: new features (like the digest engine, new modules)
- Patch: bug fixes, config tweaks
### Protected Branches
`master`, `develop`, and `gh-pages` are NEVER deleted. Branch cleanup runs bi-monthly for merged feature branches.
## Repository Structure
### Key Directories
- `docs/` — V1 source (exhaustive archive, source of truth)
- `v2-docs/` — V2 source (AI-curated elite portal, derived from V1)
- `src/` — Python pipeline source code
- `data/` — Inventory (YAML + SQL), config files, digest JSON
- `scripts/` — Utility scripts (backfill, etc.)
- `.github/workflows/` — CI/CD (15 workflows)
### Key Config Files
- `v2-mkdocs.yml` — V2 MkDocs Material configuration
- `mkdocs.yml` — V1 MkDocs configuration
- `data/inventory.yaml` / `data/inventory.sql` — Unified inventory (18K+ entries)
- `data/curation_sources.yaml` — RSS feeds and X/Twitter accounts
- `data/link_rules.yaml` — Curation policies
- `GEMINI.md` — AI mandates and learning roadmap (read by Gemini agents)
### Pipeline Modules
- `src/v2_optimizer.py` — Main rendering engine (V2VisionEngine class)
- `src/news_digest.py` — 26-category temporal digest with Gemini ranking
- `src/enrichment.py` — CNCF Landscape + GitHub activity + license detection
- `src/dedup.py` — URL/hash/title deduplication engine
- `src/agentic_curator.py` — Ingestion pipeline with AI evaluation
- `src/autonomous_discovery.py` — GitHub trending discovery (14 queries)
- `src/gemini_utils.py` — Gemini API wrapper with key rotation
- `src/inventory_manager.py` — Dual YAML+SQL inventory management
## Build and Run
### Local testing (no API keys needed)
```bash
pip install -r requirements.txt
mkdocs serve -f v2-mkdocs.yml # Preview V2 portal
mkdocs serve -f mkdocs.yml # Preview V1 portal
```
### Pipeline commands
```bash
python3 -m scripts.backfill_discovered_at # Backfill discovered_at field
python3 -m src.news_digest # Generate digest (needs Gemini API key)
python3 -m src.enrichment # CNCF + GitHub enrichment (needs GH_TOKEN)
python3 -m src.dedup # Dedup scan (dry-run by default)
python3 -m src.v2_optimizer --render-only # Render V2 portal (no AI calls)
```
## Coding Conventions
- All Python exceptions must use `except Exception as e: log_event(f"[WARN] context: {str(e)[:100]}")` — never bare `except: pass`
- Use `from src.logger import log_event` for logging
- Use `from src.config import MADRID_TZ` for timezone-aware timestamps
- Inventory fields: `discovered_at` (ISO), `last_ai_eval` (ISO), `company`, `geo_region` must be preserved during merges
- The `update_inventory_entry()` function preserves `discovered_at` — never overwrite it with new data
## URL Policy: Clean URLs, No .html Suffix
Both V1 and V2 MUST use `use_directory_urls: true` in their mkdocs.yml. This produces clean URLs like `/kubernetes/` instead of `/kubernetes.html`. **NEVER** enable the `offline` plugin — it forces `.html` suffixes on all URLs, breaking SEO and existing deep-links. This is a hard rule.
## RSS/Twitter Sources
RSS feeds are limited to those that actually work (many block bots). Don't add new RSS feeds without testing. Current working feeds are defined in `data/curation_sources.yaml`.
+28 -1
View File
@@ -376,4 +376,31 @@ The bot must rotate between profiles to avoid detection:
- **V2 Index Metrics Protocol**: The "Knowledge Architecture and AI Coverage Status" report in the V2 index MUST include a direct comparison between V1 and V2 inventory. This report MUST display: 1. **V1 Base Inventory** (Total resources in the master archive), 2. **V2 Elite Selection** (Count of candidates and the resulting density ratio), 3. **AI Enrichment Coverage**, and 4. **GitHub Metadata Coverage**. This ensures transparency in the knowledge distillation process.
- **Redundancy-Free Branding**: To ensure professional UI density, the V2 Portal header MUST NOT repeat the "Nubenetes" brand. The title MUST follow the pattern: "Nubenetes Elite Portal (V2) | Awesome Kubernetes and Cloud".
- **Decoupled Workflow Architecture**: The Agentic V2 ecosystem MUST utilize a decoupled micro-workflow structure (Health Monitor, Metadata Engine, AI Curator, and Publisher) to optimize compute quotas and minimize Gemini token consumption. Any update to the V2 rendering logic MUST use the `--render-only` flag in the Publisher pipeline to maintain execution speed.
to maintain execution speed.
## Git Workflow: Gitflow (Mandatory)
This repository uses **Gitflow**. All agents and automated processes MUST follow this branching model:
- **`master`**: Production branch. Only receives merges from `release/*` branches. Every merge to master gets a **semantic version tag** (`vX.Y.Z`) and a **GitHub Release** with detailed release notes.
- **`develop`**: Integration branch. All feature branches merge here via PR. Back-merged from master after each release.
- **`feat/*`**: Feature branches. Created from `develop`, merged back to `develop` via PR.
- **`release/vX.Y.Z`**: Release branches. Created from `develop`, merged to `master` with `--no-ff`, then back-merged to `develop`.
- **`gh-pages`**: Deployment. NEVER modified directly.
- **Protected branches**: `master`, `develop`, `gh-pages` — NEVER deleted.
### Release Sequence
1. Feature branch → PR to `develop` → merge
2. Create `release/vX.Y.Z` from `develop`
3. Merge release to `master` (`--no-ff`)
4. Create annotated tag: `git tag -a vX.Y.Z -m "..."`
5. Push master + tag
6. Back-merge master to develop (`--no-ff`)
7. Create GitHub Release with `gh release create`
### Versioning: `v{major}.{minor}.{patch}`
- **Major**: breaking changes or architectural shifts
- **Minor**: new features (modules, digest categories, new pipelines)
- **Patch**: bug fixes, config tweaks, content updates
## URL Policy: Clean URLs (Mandatory)
Both V1 (`mkdocs.yml`) and V2 (`v2-mkdocs.yml`) MUST use `use_directory_urls: true` to produce clean URLs like `/kubernetes/` instead of `/kubernetes.html`. **NEVER** enable the MkDocs `offline` plugin — it forces `.html` suffixes on all URLs, breaking SEO authority and thousands of existing deep-links. This is a hard, non-negotiable rule.
+78 -23
View File
@@ -40,7 +40,7 @@
* [5.4. The Incremental Elite Engine](#54-the-incremental-elite-engine)
* [5.5. Decoupled Knowledge Lifecycle (V2 Architecture)](#55-decoupled-knowledge-lifecycle-v2-architecture)
* [5.6. Multi-Language Support Policy](#56-multi-language-support-policy)
6. [6. The Unified Agentic Database (Knowledge Graph)](#6-the-unified-agentic-database-knowledge-graph)
6. [6. The Unified Agentic Database (Coexistence Knowledge Graph)](#6-the-unified-agentic-database-coexistence-knowledge-graph)
* [6.1. Database Components](#61-database-components)
* [6.2. The 'Database-First' Reasoning Protocol (Zero-Redundancy)](#62-the-database-first-reasoning-protocol-zero-redundancy)
* [6.3. Database Lifecycle and Hygiene](#63-database-lifecycle-and-hygiene)
@@ -135,14 +135,14 @@ Additionally, as of May 2026, Nubenetes has reached the **Platinum Operational T
## 2. Repository Metrics and Evolution
### 2.1. The "Heart" of Nubenetes
(Stats as of 2026-06-18)
(Stats as of 2026-06-19)
<!-- HEART_STATS_START -->
| Metric | Value |
| :--- | :--- |
| **Total Technical Resources (Links)** | **18647+** |
| **Specialized MD Pages** | **162** |
| **Total Commits** | **5994+** |
| **Total Commits** | **6082+** |
| **Primary AI Engine** | **Google Gemini (Agentic)** |
<!-- HEART_STATS_END -->
@@ -180,7 +180,7 @@ The growth of Nubenetes reflects the acceleration of the Cloud Native ecosystem.
| 6 | 2023 | 30 | 123 | Maintenance & Refinement |
| 7 | 2024 | 53 | 218 | Curation Strategy Pivot |
| 8 | 2025 | 5 | 20 | Stability & Research Phase |
| 9 | 2026 | 2435 | 10,056 | **Agentic AI Surge** (May 2026 Inception) |
| 9 | 2026 | 2523 | 10,419 | **Agentic AI Surge** (May 2026 Inception) |
<!-- ANNUAL_GROWTH_END -->
<!-- ANNUAL_CHART_START -->
@@ -196,8 +196,8 @@ xychart-beta
title "Nubenetes Annual Growth Metrics (20182026)"
x-axis ["2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", "2026"]
y-axis "Volume (Commits / Estimated New Refs)" 0 --> 11000
bar [1445, 586, 8449, 2193, 1660, 123, 218, 20, 10056]
bar [350, 142, 2046, 531, 402, 30, 53, 5, 2435]
bar [1445, 586, 8449, 2193, 1660, 123, 218, 20, 10419]
bar [350, 142, 2046, 531, 402, 30, 53, 5, 2523]
```
<!-- ANNUAL_CHART_END -->
@@ -207,7 +207,7 @@ xychart-beta
| :--- | :---: | :---: | :--- |
| 2026-04 | 25 | 103 | Active Curation |
| 2026-05 | 2101 | 8,677 | **Agentic Inception (Gemini Era)** |
| 2026-06 | 309 | 1,276 | Active Curation |
| 2026-06 | 397 | 1,639 | Active Curation |
<!-- MONTHLY_SURGE_END -->
### 2.4. Content Distribution and Semantic Clustering
@@ -268,6 +268,10 @@ The autonomy of Nubenetes is powered by a modern, resilient tech stack that ensu
| **Discovery** | Twikit and Playwright | Autonomous scraping and account rotation. |
| **Resilience** | Identity Rotation | Evasion of anti-bot blocks using multiple profiles. |
| **Deployment** | MkDocs Material & Native GH Pages | High-performance static site generation via native artifact deployment. |
| **Intelligence** | News Digest Engine | AI-powered temporal digest across 26 categories (3/6/12 months). |
| **Enrichment** | CNCF + GitHub Activity | Landscape graduation status, issue/PR velocity, license change detection. |
| **Dedup** | Similarity Engine | URL, content-hash, and title-similarity deduplication (85% threshold). |
| **Offline** | PWA Support | Service Worker caching for offline reading of the portal. |
---
@@ -356,7 +360,7 @@ graph TD
style C fill:#f9f,stroke:#333,stroke-width:2px
```
#### Shared Curation & Data Policies (`GEMINI.md`)
#### Shared Curation and Data Policies (`GEMINI.md`)
* **Target**: Ephemeral CI/CD runners (GitHub Actions) and local coding assistants.
* **Purpose**: Dictates *what* the repository structure, link formatting, language metadata tagging, and minimum quality levels must look like.
* **Automation integration**: Ingested by the build scripts to programmatically construct system prompts for API LLM completions.
@@ -394,11 +398,51 @@ Nubenetes operates with two distinct editions to serve different engineering nee
- **No stars**: Standard reference documentation and technical resources.
- **Multi-Dimensional Tagging (1:N):** Every resource is classified with multiple semantic tags (e.g., `[DE FACTO STANDARD]`, `[GUIDE]`, `[CASE STUDY]`, `[EMERGING]`) providing deep technical context and maturity status.
- **Minimalist Inline Summaries**: Resources feature a **"Deep-Dive"** inline tag (using native HTML5 `<details>`) that expands into a rich technical summary without consuming space when collapsed. These summaries use the **Double-Evidence Synthesis** protocol to provide verified architectural insights.
- **Semantic Cross-Linking:** The portal autonomously identifies and links related categories within the same strategic dimension (e.g., suggesting `Flux` when reading about `Argo`), creating a cohesive **Industrial Knowledge Graph**.
- **Semantic Cross-Linking:** The portal autonomously identifies and links related categories within the same strategic dimension (e.g., suggesting `Flux` when reading about `Argo`), creating a cohesive **Industrial Knowledge Graph**. Additionally, **cross-dimension "See Also" links** connect pages that share technical tags across different dimensions.
- **Executive Context**: Every strategic dimension features an AI-generated **State-of-the-Art Introduction** providing high-level architectural context and industry direction before the link listings.
- **Source of Truth:** The `v2-docs/` directory (Derived from V1).
- **Deployment:** [nubenetes.com/v2/](https://nubenetes.com/v2)
#### V2 Intelligence Digest (June 2026)
The V2 portal includes an **AI-powered Intelligence Digest** system that surfaces the most relevant resources from the last 3, 6, and 12 months across **26 curated categories**:
| Category Group | Categories |
| :--- | :--- |
| **Tech Core (9)** | Kubernetes & Orchestration, Containers & Runtime, Networking & Service Mesh, Architecture & Microservices, Data/Messaging/Storage, AI & Agents, MLOps & Data Science, Python/Java/Dev Ecosystem, Linux & System Foundations |
| **Platform & Ops (8)** | Security & Compliance, Infrastructure as Code, CI/CD & GitOps, Observability/SRE/Testing, DevOps & Culture, Platform Engineering & DevEx, FinOps & Cloud Cost, Certification & Training |
| **Cloud & Enterprise (5)** | AWS, Azure, GCP/OCI/Others, OpenShift/Red Hat, Virtualization & Private Cloud (VMware/Broadcom, Proxmox, Nutanix, KubeVirt) |
| **Industry / Geo (4)** | Americas, Europe, Spain, Asia-Pacific |
**Key features:**
- **Trending Now** cards on the index page with the top cross-category items ranked by Gemini AI
- **Dedicated digest pages** (`tech-digest.md`, `industry-digest.md`) with tabbed 3/6/12 month views
- **Temporal tracking** via `discovered_at` field on all 18,000+ inventory entries
- **Company & geo-region classification** extracted by Gemini during ingestion for industry digest
- **Automatic staleness detection**: entries enriched >6 months ago are re-evaluated by AI (`last_ai_eval`)
#### V2 Data Quality and Pipeline Hardening (June 2026)
- **CNCF Landscape Integration** (`src/enrichment.py`): Auto-fetches graduation status (Sandbox/Incubating/Graduated/Archived) for CNCF projects to power maturity tags.
- **GitHub Activity Enrichment**: Fetches issue/PR velocity and assigns community health scores (active/healthy/low/dormant).
- **License Change Detection**: Compares stored licenses with current GitHub data, flagging high-impact changes (e.g., BSL, SSPL switches).
- **Deduplication Engine** (`src/dedup.py`): URL normalization, content-hash matching, and title-similarity detection (85% threshold) to eliminate duplicate entries.
- **Exception Observability**: All 50+ bare `except: pass` patterns across the pipeline replaced with contextual logging.
- **Expanded Discovery**: Autonomous GitHub trending discovery expanded from 6 to 14 search queries covering DevOps, observability, security, IaC, databases, CI/CD, service mesh, and platform engineering.
- **Stale Health Re-check**: Online entries older than 30 days are automatically re-validated instead of being skipped.
#### V2 MkDocs Material Enhancements (June 2026)
- **Instant Navigation** with prefetch for SPA-like experience across 140+ pages
- **Breadcrumbs** (`navigation.path`) for orientation in deep category hierarchies
- **Announcement Bar** promoting the Intelligence Digest
- **Tags Plugin** for native clickable cross-page tag navigation
- **RSS Feed** for digest page subscription
- **PWA/Offline Support** for cached offline reading
- **Minify Plugin** for production HTML optimization
- **12 Stub Pages Merged** into parent categories with automatic redirects (e.g., `react.md``javascript.md`, `chef.md``ansible.md`, `oauth.md``securityascode.md`)
#### V2 URL Policy (June 2026)
- **Clean URLs enforced**: Both V1 and V2 use `use_directory_urls: true` producing SEO-friendly URLs (e.g., `/kubernetes/` not `/kubernetes.html`).
- **Offline plugin permanently removed**: The MkDocs `offline` plugin forces `.html` suffixes on all URLs, breaking thousands of existing deep-links and SEO authority. It is explicitly forbidden in both `CLAUDE.md` and `GEMINI.md` mandates.
### 5.3. Architecture Comparison Matrix: V1 vs. V2
To better understand the dual-nature of the project, the following matrix details the technical and philosophical differences between the two editions:
@@ -490,24 +534,31 @@ To embrace the diverse global Cloud Native community while maintaining internati
---
## 6. The Unified Agentic Database (Knowledge Graph)
## 6. The Unified Agentic Database (Coexistence Knowledge Graph)
Nubenetes now utilizes a **Unified Metadata Architecture** to maintain consistency across V1 and V2 while optimizing AI performance. All links are indexed in a local YAML database that serves as the **Persistent Memory** for our autonomous agents.
Nubenetes now utilizes a **Unified SQL & YAML Database Architecture** to maintain consistency across V1 and V2 while optimizing agentic operations and repository efficiency. All curated links and metadata are managed via a coexisting local database engine.
### 6.1. Database Components
1. **Central Inventory ([`data/inventory.yaml`](data/inventory.yaml))**: The universal single source of truth for technical metadata and resource lifecycle.
* **Core Data**: `title`, `year`, `stars` (0-5), `description` (V1 Native), `ai_summary` (V2 English), `category`.
* **Structural Intelligence**: `hierarchy` (Recursive list up to 10 levels), `v1_locations`, `v2_locations`.
* **Platinum Lifecycle**: `content_hash` (SHA256), `health_score` (0-100), `source_provenance`, `social_preview_url`, `mentions_count`.
### 6.1. Database Components and SQLite Engine (Option 3 Coexistence)
To guarantee backward compatibility and Git efficiency, the system operates on a dual-save database coexistence model:
1. **SQLite Database & SQL Text ([`data/inventory.sql`](data/inventory.sql))**: The Git source-of-truth. During execution, the SQL script compiles into a temporary in-memory SQLite database, enabling full relational schema access and SQL query optimization. On save, SQLite's native `iterdump()` decompiles it back into a flat SQL text database file where each resource insert occupies a single line for perfect git diff readability.
2. **Central Backup Inventory ([`data/inventory.yaml`](data/inventory.yaml))**: Automatically synchronized during database saves. Serves as a backward-compatible interface for legacy markdown parsing scripts.
3. **High-Speed Parsing (C-Loader Integration)**: Direct YAML parsing utilizes high-speed native C-extensions (`yaml.CSafeLoader` and `yaml.CSafeDumper`) across all Python scripts (e.g. `v2_optimizer.py`, `reorganize_mosaic.py`, `safety_guard.py`) for a 10x-20x speedup in parsing operations.
#### 6.1.2. Platinum Lifecycle Schema
* **Core Data**: `url` (Primary Key), `title`, `year`, `stars` (0-5), `description` (V1 Native), `ai_summary` (V2 English), `category`.
* **Structural Intelligence**: `hierarchy` (Recursive JSON list), `tags` (JSON list), `v1_locations`, `v2_locations`, `youtube_mosaic` (JSON dict).
* **Platinum Lifecycle**: `content_hash` (SHA256 fingerprint), `health_score` (0-100), `source_provenance`, `social_preview_url`, `mentions_count`, `addition_method`.
### 6.2. The 'Database-First' Reasoning Protocol (Zero-Redundancy)
To maximize economic efficiency and maintain the **30-minute execution standard**, all AI agents follow a **Database-First** and **Zero-Redundancy** protocol:
1. **Local Lookup**: Before initiating any Gemini call, the agent checks if the URL is already indexed in [`data/inventory.yaml`](data/inventory.yaml).
2. **Zero-Redundancy Pipeline**: The V2 Optimizer leverages health and metadata (`gh_stars`, `gh_license`) already validated by the `IntelligentLinkCleaner`. If a resource is marked as `status: online` and has recent metadata, V2 bypasses redundant network checks.
3. **Smart Grounding (Search Retrieval)**: AI agents only activate grounding-heavy calls (Google Search) for resources that are new, missing metadata, or flagged for `needs_ai_refresh`. This reduces latencia by >80% for 15k+ link archives.
4. **Insight Reuse**: If the resource exists with valid metadata, the agent **reuses existing insights**, reducing API traffic to zero.
5. **Memory Efficiency Tracking**: The system tracks **Cache Hit Ratios** and **Estimated Token Savings** in every Intelligence Report.
6. **Mandatory Persistence**: Modified YAML files are automatically injected into Pull Requests, ensuring that "System Memory" is version-controlled and shared across all workflows.
1. **Local Lookup**: Before initiating any Gemini call, the agent queries the compiled SQLite/SQL database to see if the URL is already indexed.
2. **Domain Reputation Registry**: In `main.py`, scraping/health-check success rates are recorded under `domain_reputation` inside `health_learning.json` for adaptive timeout and scraping rotation.
3. **Stateful Debate Caching**: In `v2_debate.py`, consensus evaluations for borderline resources are cached based on the SHA256 hash of their combined metadata (`title`, `description`, `tags`). On cache hits, the agent skips redundant LLM calls and retrieves the score directly.
4. **Pre-Commit Markdown Lint Hook**: In `src/pre_commit_schema_check.py`, a local Git pre-commit hook automatically runs on developer changes to enforce heading rules (no emojis/ampersands in titles), protocol integrity, link bracket spacing, and duplicate checks in docs markdown.
5. **Insight Reuse**: If the resource exists with valid metadata, the agent **uses existing insights**, reducing API traffic to zero.
6. **Memory Efficiency Tracking**: The system tracks **Cache Hit Ratios** and **Estimated Token Savings** in every Intelligence Report.
7. **Mandatory Persistence**: Modified databases are automatically injected into Pull Requests, ensuring that "System Memory" is version-controlled and shared across all workflows.
### 6.3. Database Lifecycle and Hygiene
To maintain a high-performance "Single Source of Truth", Nubenetes implements automated hygiene protocols:
@@ -1183,7 +1234,11 @@ To maintain transparency and ease of navigation, all key configuration, database
- **Health Check Logic:** [`src/intelligent_health_checker.py`](src/intelligent_health_checker.py) - Link rot prevention and canonical updates.
- **Twikit Ingestion:** [`src/ingestion_twikit.py`](src/ingestion_twikit.py) - X.com scraping and account rotation logic.
- **Backup Ingestion:** [`src/ingestion_backup.py`](src/ingestion_backup.py) - Manual and historical JSON data processing.
- **Discovery Engine:** [`src/autonomous_discovery.py`](src/autonomous_discovery.py) - Multi-source technical news extraction.
- **Discovery Engine:** [`src/autonomous_discovery.py`](src/autonomous_discovery.py) - Multi-source technical news extraction (14 GitHub search queries).
- **News Digest Engine:** [`src/news_digest.py`](src/news_digest.py) - AI-powered temporal digest across 26 categories with Gemini ranking (3/6/12 months).
- **Enrichment Pipeline:** [`src/enrichment.py`](src/enrichment.py) - CNCF Landscape integration, GitHub activity enrichment, and license change detection.
- **Deduplication Engine:** [`src/dedup.py`](src/dedup.py) - URL normalization, content-hash, and title-similarity dedup (85% threshold).
- **Backfill Utility:** [`scripts/backfill_discovered_at.py`](scripts/backfill_discovered_at.py) - One-shot `discovered_at` population for existing entries.
- **Gemini Utils:** [`src/gemini_utils.py`](src/gemini_utils.py) - AI model discovery, rate limiting, and session tracking.
- **Markdown Logic:** [`src/markdown_ast.py`](src/markdown_ast.py) - Sophisticated parsing of repository content.
- **Observability:** [`src/logger.py`](src/logger.py) | [`src/report_generator.py`](src/report_generator.py) - Execution transparency and visual reporting.
+19615
View File
File diff suppressed because it is too large Load Diff
+282319 -151867
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block announce %}
<strong>New:</strong> <a href="./tech-digest/">Intelligence Digest</a> — AI-curated trending resources across 26 categories
{% endblock %}
+271 -2
View File
@@ -3,6 +3,15 @@
* Color Palette: Deep Space Black & Neon Cyan
*/
html {
scroll-behavior: smooth;
}
.md-content {
max-width: 1600px;
margin: 0 auto;
}
:root {
/* LIGHT MODE - Modern, Crisp, High Contrast */
--md-primary-fg-color: #09090b; /* Zinc 950 */
@@ -343,6 +352,22 @@ a {
border-color: #2dd4bf !important;
}
.hero-badge-card--amber {
border-color: rgba(245, 158, 11, 0.2);
background: rgba(245, 158, 11, 0.02);
}
.hero-badge-card--amber:hover {
background: rgba(245, 158, 11, 0.06) !important;
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.18);
border-color: #f59e0b !important;
}
.hero-badge-icon {
font-size: 2rem;
line-height: 1;
margin-bottom: 4px;
}
.hero-badge-title {
font-weight: bold;
font-size: 0.95rem;
@@ -355,6 +380,56 @@ a {
margin-top: 4px;
}
/* Intelligence Digest mini-preview on index */
.digest-preview {
background: rgba(245, 158, 11, 0.04);
border: 1px solid rgba(245, 158, 11, 0.15);
border-radius: 12px;
padding: 20px 24px;
margin: 24px 0;
}
.digest-preview-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.digest-preview-title {
font-weight: 700;
font-size: 1rem;
color: var(--md-primary-fg-color);
}
.digest-preview-link {
font-size: 0.8rem;
color: #f59e0b;
text-decoration: none;
font-weight: 600;
letter-spacing: 0.02em;
}
.digest-preview-link:hover { text-decoration: underline; }
.digest-preview-list {
list-style: none;
margin: 0;
padding: 0;
}
.digest-preview-list li {
padding: 5px 0;
border-bottom: 1px solid rgba(245, 158, 11, 0.08);
font-size: 0.875rem;
display: flex;
align-items: center;
gap: 8px;
}
.digest-preview-list li:last-child { border-bottom: none; }
.digest-preview-cat {
font-size: 0.7rem;
color: #f59e0b;
background: rgba(245, 158, 11, 0.1);
border-radius: 4px;
padding: 1px 6px;
white-space: nowrap;
}
/* Hero Showcase Image wrapper (4 cars in a container) */
.hero-showcase-wrapper {
margin: 24px auto;
@@ -807,6 +882,13 @@ a {
contain-intrinsic-size: auto 500px;
}
/* Defer rendering of large resource lists in category pages */
.md-typeset > ul,
.md-typeset > ol {
content-visibility: auto;
contain-intrinsic-size: auto 200px;
}
/* Collapsible tag lists on the tags index page */
.v2-tag-section details {
border: 1px solid rgba(0, 0, 0, 0.08);
@@ -908,13 +990,200 @@ input[type="text"] {
/* Coarse touch pointer optimizations (Mobile AA targets) */
@media (pointer: coarse) {
.md-tag,
.md-button,
.md-tag,
.md-button,
.v2-tag-section summary {
min-block-size: 44px;
}
}
/* ---------------------------------------------------- */
/* ANNOUNCEMENT BAR */
/* ---------------------------------------------------- */
.md-banner {
background: linear-gradient(135deg, #0ea5e9, #22d3ee);
color: #ffffff;
font-weight: 500;
text-align: center;
padding: 8px 16px;
}
.md-banner a {
color: #ffffff;
text-decoration: underline;
font-weight: 700;
}
[data-md-color-scheme="slate"] .md-banner {
background: linear-gradient(135deg, #0284c7, #06b6d4);
}
/* ---------------------------------------------------- */
/* TRENDING NOW — NEWS DIGEST CARDS */
/* ---------------------------------------------------- */
.trending-section {
margin: 32px 0;
padding: 24px;
border-radius: 16px;
border: 1px solid rgba(14, 165, 233, 0.15);
background: linear-gradient(180deg, rgba(14, 165, 233, 0.03) 0%, transparent 100%);
}
[data-md-color-scheme="slate"] .trending-section {
border-color: rgba(34, 211, 238, 0.15);
background: linear-gradient(180deg, rgba(34, 211, 238, 0.05) 0%, transparent 100%);
}
.trending-section__title {
font-size: 1.4em;
font-weight: 700;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.trending-section__updated {
font-size: 0.55em;
font-weight: 500;
padding: 2px 10px;
border-radius: 20px;
background: rgba(14, 165, 233, 0.12);
color: var(--md-accent-fg-color);
border: 1px solid rgba(14, 165, 233, 0.25);
letter-spacing: 0.02em;
}
.trending-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
margin: 16px 0;
}
.trending-card {
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 12px;
padding: 16px 20px;
background: var(--md-primary-bg-color--light);
transition: transform 0.2s ease, box-shadow 0.2s ease;
position: relative;
overflow: hidden;
}
.trending-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(14, 165, 233, 0.12);
}
[data-md-color-scheme="slate"] .trending-card {
border-color: rgba(255, 255, 255, 0.08);
background: rgba(24, 24, 27, 0.4);
}
[data-md-color-scheme="slate"] .trending-card:hover {
box-shadow: 0 8px 24px rgba(34, 211, 238, 0.12);
}
.trending-card__category {
font-size: 0.75em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--md-accent-fg-color);
margin-bottom: 8px;
}
.trending-card__title {
font-size: 0.95em;
font-weight: 600;
margin-bottom: 6px;
line-height: 1.3;
}
.trending-card__title a {
color: inherit;
text-decoration: none;
}
.trending-card__title a:hover {
color: var(--md-accent-fg-color);
}
.trending-card__meta {
font-size: 0.8em;
color: var(--md-primary-fg-color--dark);
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.trending-card__why {
font-size: 0.82em;
line-height: 1.45;
color: var(--md-primary-fg-color--light);
}
.trending-card__impact {
position: absolute;
top: 12px;
right: 12px;
font-size: 0.75em;
font-weight: 700;
padding: 2px 8px;
border-radius: 6px;
}
.trending-card__impact--critical {
background: rgba(239, 68, 68, 0.15);
color: #ef4444;
}
.trending-card__impact--high {
background: rgba(245, 158, 11, 0.15);
color: #f59e0b;
}
.trending-card__impact--medium {
background: rgba(14, 165, 233, 0.15);
color: #0ea5e9;
}
/* Digest link cards (CTA to full digest pages) */
.digest-links {
display: flex;
gap: 16px;
margin-top: 20px;
flex-wrap: wrap;
}
.digest-link-card {
flex: 1;
min-width: 240px;
padding: 16px 24px;
border-radius: 12px;
border: 2px solid var(--md-accent-fg-color);
text-align: center;
text-decoration: none;
color: inherit;
font-weight: 600;
transition: all 0.2s ease;
}
.digest-link-card:hover {
background: var(--md-accent-fg-color);
color: #ffffff;
transform: translateY(-2px);
}
[data-md-color-scheme="slate"] .digest-link-card:hover {
color: #09090b;
}
+4 -3
View File
@@ -13,9 +13,10 @@ document.addEventListener("DOMContentLoaded", function () {
// Do not show on the homepage, video hub index page, or technical tags index page (performance)
const h1 = contentArea.querySelector("h1");
if (h1 && (
h1.textContent.includes("Nubenetes Elite Portal (V2)") ||
h1.textContent.includes("Agentic Video Hub") ||
h1.textContent.includes("Technical Tags Index")
h1.textContent.includes("Nubenetes Elite Portal (V2)") ||
h1.textContent.includes("Agentic Video Hub") ||
h1.textContent.includes("Technical Tags Index") ||
h1.textContent.includes("Intelligence Digest")
)) {
return;
}
+1
View File
@@ -4,3 +4,4 @@ yt-dlp
youtube-transcript-api
mkdocs-redirects>=1.2.3
mkdocs-minify-plugin>=0.8.0
mkdocs-rss-plugin>=1.15.0
+59
View File
@@ -0,0 +1,59 @@
"""
Backfill script: adds discovered_at to inventory entries that lack it.
Priority: gh_pushed > last_checked > year > default "2024-01-01T00:00:00"
Run once: python -m scripts.backfill_discovered_at
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from datetime import datetime
from src.inventory_manager import load_inventory, save_inventory
from src.config import MADRID_TZ
def backfill():
inv = load_inventory()
updated = 0
total = 0
for url, entry in inv.items():
if not isinstance(entry, dict):
continue
total += 1
if entry.get("discovered_at"):
continue
discovered = None
gh_pushed = entry.get("gh_pushed")
if gh_pushed and isinstance(gh_pushed, str) and len(gh_pushed) >= 10:
discovered = gh_pushed
if not discovered:
last_checked = entry.get("last_checked")
if isinstance(last_checked, (int, float)) and last_checked > 0:
try:
discovered = datetime.fromtimestamp(last_checked, tz=MADRID_TZ).isoformat()
except (OSError, ValueError):
pass
if not discovered:
year = entry.get("year", "")
if isinstance(year, str) and year.isdigit() and len(year) == 4:
discovered = f"{year}-06-01T00:00:00+02:00"
elif isinstance(year, int):
discovered = f"{year}-06-01T00:00:00+02:00"
if not discovered:
discovered = "2024-01-01T00:00:00+02:00"
entry["discovered_at"] = discovered
updated += 1
print(f"Backfill complete: {updated}/{total} entries updated with discovered_at")
save_inventory(inv)
if __name__ == "__main__":
backfill()
+34 -17579
View File
File diff suppressed because it is too large Load Diff
+21 -6
View File
@@ -38,7 +38,8 @@ async def _get_github_activity(url: str) -> Dict:
"gh_pushed": data.get("pushed_at"),
"gh_license": data.get("license", {}).get("spdx_id", "N/A")
}
except: pass
except Exception as e:
log_event(f"[WARN] fetch GitHub activity for {url}: {str(e)[:100]}")
return {}
async def _deep_fetch_content(url: str) -> Tuple[str, Dict]:
@@ -61,7 +62,8 @@ async def _deep_fetch_content(url: str) -> Tuple[str, Dict]:
img_match = re.search(r'meta property="og:image" content="(.*?)"', resp.text)
if img_match: og_image = img_match.group(1)
return resp.text, {"og_image": og_image}
except: pass
except Exception as e:
log_event(f"[WARN] deep fetch content for {url}: {str(e)[:100]}")
return "", {}
async def evaluate_extracted_assets(raw_assets: List[Dict]) -> Dict[str, Dict]:
@@ -76,7 +78,8 @@ async def evaluate_extracted_assets(raw_assets: List[Dict]) -> Dict[str, Dict]:
try:
memory_data = json.load(open(memory_file, "r"))
domain_blacklist = set(memory_data.get("blacklisted_domains", []))
except: pass
except Exception as e:
log_event(f"[WARN] load blacklist from health_learning.json: {str(e)[:100]}")
# 1. Pre-filter
for asset in raw_assets:
@@ -140,7 +143,10 @@ async def evaluate_extracted_assets(raw_assets: List[Dict]) -> Dict[str, Dict]:
"- Assign tags. You MUST include:\n"
" 1. 1 to 2 maturity tags from: [DE FACTO STANDARD], [ENTERPRISE-STABLE], [EMERGING], [GUIDE], [CASE STUDY], [COMMUNITY-TOOL], [LEGACY].\n"
" 2. Fine-grained technical/architectural tags from the content (e.g., [EBPF], [WASM], [GITOPS], [IAC], [SERVICE-MESH], [SERVERLESS], [MLOPS], [DB]). Keep them uppercase and wrapped in brackets.\n"
"Respond ONLY JSON list: [{\"url\": \"...\", \"impact_score\": int, \"reputation_penalty\": bool, \"reputation_summary\": \"...\", \"pub_date\": \"YYYY-MM-DD\", \"primary_category\": \"...\", \"suggested_new_category\": \"...\", \"title\": \"...\", \"desc\": \"...\", \"en_summary\": \"High-density summary...\", \"language\": \"...\", \"type\": \"...\", \"level\": \"...\", \"technical_hierarchy\": [...], \"tags\": [...], \"is_microservice\": bool}, ...]\n\n"
"PHASE 5: COMPANY & GEO CLASSIFICATION\n"
"- Identify 'company': The company/organization that authored or is the primary subject (e.g., 'Google', 'Netflix', 'CNCF', 'Independent').\n"
"- Identify 'geo_region': The HQ region of that company. Use one of: 'americas', 'europe', 'spain', 'asia_pacific', 'global'.\n"
"Respond ONLY JSON list: [{\"url\": \"...\", \"impact_score\": int, \"reputation_penalty\": bool, \"reputation_summary\": \"...\", \"pub_date\": \"YYYY-MM-DD\", \"primary_category\": \"...\", \"suggested_new_category\": \"...\", \"title\": \"...\", \"desc\": \"...\", \"en_summary\": \"High-density summary...\", \"language\": \"...\", \"type\": \"...\", \"level\": \"...\", \"technical_hierarchy\": [...], \"tags\": [...], \"is_microservice\": bool, \"company\": \"...\", \"geo_region\": \"...\"}, ...]\n\n"
"RESOURCES:\n" + "\n".join([f"- {d['asset']['url']}: (MVQ Penalty: {d['mvq_penalty']}) {d['content']}" for d in batch_data])
)
@@ -205,9 +211,16 @@ async def evaluate_extracted_assets(raw_assets: List[Dict]) -> Dict[str, Dict]:
"reputation_status": "Vetted" if not data.get("reputation_penalty") else "Suspicious",
"reputation_summary": data.get("reputation_summary", ""),
"source_provenance": d["asset"].get("source_type", "Social"), "social_preview_url": d["rich_meta"].get("og_image", ""),
"company": data.get("company", ""), "geo_region": data.get("geo_region", ""),
"category": primary_cat, "status": "online", "last_checked": datetime.now().timestamp(),
"discovered_at": datetime.now(MADRID_TZ).isoformat(),
"last_ai_eval": datetime.now(MADRID_TZ).isoformat(),
"suggested_new_category": data.get("suggested_new_category", ""),
"addition_method": "automatic", **d["gh_meta"]
"addition_method": {
"rss": "rss_ingestion", "GitHub Trending": "github_trending",
"Twitter": "twitter_ingestion", "nubenetes": "manual"
}.get(d["asset"].get("source_type", ""), "automatic"),
**d["gh_meta"]
}
if "youtube.com" in url or "youtu.be" in url:
title_desc = f"{data['title']} {data['desc']}".lower()
@@ -250,7 +263,9 @@ class AgenticCurator:
prompt = "Identify 5 high-quality Cloud Native or K8s engineering blogs or 'Awesome' repos active in 2026. Return ONLY JSON list of URLs."
try:
return await call_gemini_with_retry(prompt, use_grounding=True)
except: return []
except Exception as e:
log_event(f"[WARN] autonomous source discovery: {str(e)[:100]}")
return []
async def decide_smart_injection(self, content: str, asset: Dict) -> str:
# Extract headers from the markdown content
+18 -5
View File
@@ -2,20 +2,31 @@ import aiohttp
import json
import httpx
import re
from src.config import GEMINI_API_KEY, NUBENETES_CATEGORIES
from src.config import GEMINI_API_KEY, NUBENETES_CATEGORIES, GH_TOKEN
from src.gemini_utils import call_gemini_with_retry
from src.logger import log_event
async def fetch_github_trending_cloud_native() -> list[dict]:
queries = [
"topic:kubernetes+stars:>1000",
"topic:mcp-server+stars:>0",
"topic:kubernetes+stars:>1000",
"topic:mcp-server+stars:>0",
"topic:model-context-protocol+stars:>0",
"topic:ai-agents+stars:>50",
"awesome+stars:>1000",
"topic:generative-ai+stars:>500"
"topic:generative-ai+stars:>500",
"topic:devops+stars:>500",
"topic:observability+stars:>200",
"topic:cloud-security+stars:>200",
"topic:terraform+stars:>500",
"topic:database+stars:>500",
"topic:cicd+stars:>200",
"topic:service-mesh+stars:>100",
"topic:platform-engineering+stars:>100",
]
all_repos = []
headers = {'Accept': 'application/vnd.github.v3+json'}
if GH_TOKEN:
headers['Authorization'] = f'token {GH_TOKEN}'
async with aiohttp.ClientSession(headers=headers) as session:
for q in queries:
url = f"https://api.github.com/search/repositories?q={q}&sort=updated&order=desc"
@@ -29,7 +40,9 @@ async def fetch_github_trending_cloud_native() -> list[dict]:
"url": repo['html_url'],
"desc": repo['description'] or "No description provided."
})
except: continue
except Exception as e:
log_event(f"[WARN] GitHub search query '{q}': {str(e)[:100]}")
continue
return all_repos
async def discover_trending_assets() -> list[dict]:
+194
View File
@@ -0,0 +1,194 @@
from __future__ import annotations
import re
import asyncio
from collections import defaultdict
from difflib import SequenceMatcher
from typing import Dict, List, Tuple
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
from src.inventory_manager import load_inventory, save_inventory
from src.logger import log_event
TRACKING_PARAMS = {"utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term",
"ref", "source", "fbclid", "gclid", "mc_cid", "mc_eid", "s", "share"}
def normalize_url_deep(url: str) -> str:
parsed = urlparse(url.strip().lower())
scheme = "https"
netloc = parsed.netloc.removeprefix("www.")
path = parsed.path.rstrip("/") or "/"
params = parse_qs(parsed.query)
clean_params = {k: v for k, v in params.items() if k not in TRACKING_PARAMS}
query = urlencode(clean_params, doseq=True) if clean_params else ""
return urlunparse((scheme, netloc, path, "", query, ""))
def normalize_title(title: str) -> str:
if not title:
return ""
t = title.lower().strip()
t = re.sub(r'^[\w.-]+\.\w{2,}:\s*', '', t)
t = re.sub(r'[^\w\s]', ' ', t)
t = re.sub(r'\s+', ' ', t).strip()
return t
def find_url_duplicates(inventory: Dict) -> List[Tuple[str, str]]:
norm_map = defaultdict(list)
for url in inventory:
if not isinstance(inventory[url], dict):
continue
deep = normalize_url_deep(url)
norm_map[deep].append(url)
duplicates = []
for norm, urls in norm_map.items():
if len(urls) > 1:
for i in range(1, len(urls)):
duplicates.append((urls[0], urls[i]))
return duplicates
def find_hash_duplicates(inventory: Dict) -> List[List[str]]:
hash_map = defaultdict(list)
for url, entry in inventory.items():
if not isinstance(entry, dict):
continue
ch = entry.get("content_hash")
if ch and ch != "N/A":
hash_map[ch].append(url)
return [urls for urls in hash_map.values() if len(urls) > 1]
def find_title_duplicates(inventory: Dict, threshold: float = 0.85) -> List[Tuple[str, str, float]]:
entries = []
for url, entry in inventory.items():
if not isinstance(entry, dict):
continue
title = entry.get("title", "")
norm = normalize_title(title)
if len(norm) < 10:
continue
entries.append((url, norm, entry.get("stars") or 0))
log_event(f"[Dedup] Building title index for {len(entries)} entries...")
prefix_groups = defaultdict(list)
for url, norm, stars in entries:
words = norm.split()
prefix = " ".join(words[:3]) if len(words) >= 3 else norm
prefix_groups[prefix].append((url, norm, stars))
duplicates = []
checked = 0
for prefix, group in prefix_groups.items():
if len(group) < 2:
continue
for i in range(len(group)):
for j in range(i + 1, len(group)):
url1, norm1, stars1 = group[i]
url2, norm2, stars2 = group[j]
if stars1 >= 4 and stars2 >= 4:
continue
ratio = SequenceMatcher(None, norm1, norm2).ratio()
if ratio >= threshold:
duplicates.append((url1, url2, ratio))
checked += 1
if checked % 500 == 0:
log_event(f"[Dedup] Checked {checked}/{len(prefix_groups)} prefix groups...")
log_event(f"[Dedup] Title scan complete: {len(duplicates)} potential duplicates found")
return duplicates
def _entry_score(entry: Dict) -> Tuple:
return (
entry.get("stars") or 0,
1 if entry.get("ai_summary") else 0,
1 if entry.get("hierarchy") else 0,
len(entry.get("tags", [])),
-len(str(entry.get("url", "")))
)
def resolve_duplicates(inventory: Dict, duplicate_pairs: List[Tuple[str, str, float]]) -> int:
resolved = 0
seen = set()
for url1, url2, score in sorted(duplicate_pairs, key=lambda x: -x[2]):
if url1 in seen or url2 in seen:
continue
entry1 = inventory.get(url1, {})
entry2 = inventory.get(url2, {})
if not isinstance(entry1, dict) or not isinstance(entry2, dict):
continue
score1 = _entry_score(entry1)
score2 = _entry_score(entry2)
if score1 >= score2:
winner, loser = url1, url2
else:
winner, loser = url2, url1
inventory[loser]["status"] = "duplicate"
inventory[loser]["duplicate_of"] = winner
seen.add(loser)
resolved += 1
return resolved
async def run_dedup(dry_run: bool = True) -> Dict:
log_event("STARTING DEDUPLICATION SCAN", section_break=True)
inventory = load_inventory()
url_dups = find_url_duplicates(inventory)
log_event(f"[Dedup] URL duplicates: {len(url_dups)}")
hash_groups = find_hash_duplicates(inventory)
hash_dups = []
for group in hash_groups:
for i in range(1, len(group)):
hash_dups.append((group[0], group[i], 1.0))
log_event(f"[Dedup] Content hash duplicates: {len(hash_dups)}")
title_dups = find_title_duplicates(inventory)
all_dups = [(u1, u2, 1.0) for u1, u2 in url_dups] + hash_dups + title_dups
unique_pairs = {}
for u1, u2, s in all_dups:
key = tuple(sorted([u1, u2]))
if key not in unique_pairs or s > unique_pairs[key]:
unique_pairs[key] = s
deduped_pairs = [(k[0], k[1], v) for k, v in unique_pairs.items()]
stats = {
"url_duplicates": len(url_dups),
"hash_duplicates": len(hash_dups),
"title_duplicates": len(title_dups),
"total_unique_pairs": len(deduped_pairs),
}
if dry_run:
log_event(f"[Dedup] DRY RUN — {len(deduped_pairs)} duplicates found, no changes made")
for u1, u2, score in sorted(deduped_pairs, key=lambda x: -x[2])[:20]:
t1 = inventory.get(u1, {}).get("title", "?")[:60]
t2 = inventory.get(u2, {}).get("title", "?")[:60]
log_event(f" [{score:.0%}] {t1} <-> {t2}")
else:
resolved = resolve_duplicates(inventory, deduped_pairs)
stats["resolved"] = resolved
save_inventory(inventory)
log_event(f"[Dedup] Resolved {resolved} duplicates")
log_event(f"DEDUP COMPLETE: {stats}")
return stats
if __name__ == "__main__":
asyncio.run(run_dedup(dry_run=True))
+3 -4
View File
@@ -5,6 +5,7 @@ import asyncio
import httpx
from src.logger import log_event
from src.gemini_utils import call_gemini_with_retry, fetch_youtube_metadata
from src.inventory_manager import load_inventory, save_inventory
INVENTORY_PATH = "data/inventory.yaml"
@@ -72,8 +73,7 @@ async def main():
force_enrich = os.getenv("FORCE_ENRICH", "false").lower() == "true"
with open(INVENTORY_PATH, "r") as f:
inventory = yaml.safe_load(f)
inventory = load_inventory()
video_urls = [u for u, e in inventory.items() if e.get("is_featured_video")]
@@ -99,8 +99,7 @@ async def main():
await asyncio.gather(*batch)
# Incremental Persistence: Save after each batch
with open(INVENTORY_PATH, "w") as f:
yaml.dump(inventory, f, sort_keys=False, allow_unicode=True)
save_inventory(inventory)
log_event(f" [💾] Saved progress: {min(i + batch_size, len(tasks))}/{len(tasks)} videos.")
if i + batch_size < len(tasks):
+373
View File
@@ -0,0 +1,373 @@
from __future__ import annotations
import asyncio
import re
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
import httpx
from src.config import GH_TOKEN, MADRID_TZ
from src.logger import log_event
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
CNCF_LANDSCAPE_URL = "https://landscape.cncf.io/api/items" # Legacy, now SPA — fallback to GitHub topic search
GITHUB_API_BASE = "https://api.github.com"
GITHUB_RATE_DELAY = 0.5 # seconds between GitHub API calls (5000/hr limit = ~1.4/s safe)
MAX_REPOS_DEFAULT = 200 # cap per run — 200 × 0.5s = ~100s, well within CI timeout
ACTIVITY_STALENESS_DAYS = 30
# Community health thresholds
HEALTH_ACTIVE = 50
HEALTH_HEALTHY = 10
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _github_headers() -> Dict[str, str]:
"""Build GitHub API request headers with optional auth."""
headers = {"Accept": "application/vnd.github.v3+json"}
if GH_TOKEN:
headers["Authorization"] = f"token {GH_TOKEN}"
return headers
def _parse_github_repo(url: str) -> Optional[str]:
"""Extract 'owner/repo' from a GitHub URL. Returns None if not a GitHub URL."""
match = re.search(r"github\.com/([^/]+/[^/]+)", url)
if not match:
return None
repo = match.group(1).split("#")[0].split("?")[0].rstrip("/")
# Strip trailing .git if present
if repo.endswith(".git"):
repo = repo[:-4]
return repo
def _normalize_repo_url(url: str) -> str:
"""Normalize a GitHub repo URL for comparison (lowercase, no trailing slash)."""
url = url.lower().rstrip("/")
if url.endswith(".git"):
url = url[:-4]
# Strip protocol
url = re.sub(r"^https?://", "", url)
return url
def _is_activity_stale(entry: Dict) -> bool:
"""Check whether an entry's activity data is older than ACTIVITY_STALENESS_DAYS."""
checked = entry.get("gh_activity_checked")
if not checked:
return True
try:
checked_dt = datetime.fromisoformat(checked)
return datetime.now(MADRID_TZ) - checked_dt > timedelta(days=ACTIVITY_STALENESS_DAYS)
except (ValueError, TypeError):
return True
# ---------------------------------------------------------------------------
# Module 1: CNCF Landscape Status
# ---------------------------------------------------------------------------
async def fetch_cncf_landscape() -> Dict[str, str]:
"""Fetch CNCF project graduation status via GitHub topic search.
The legacy landscape.cncf.io/api/items endpoint is now a SPA and no
longer returns JSON. Instead, we search GitHub for repos with CNCF
maturity topics (cncf-sandbox, cncf-incubating, cncf-graduated).
Returns dict mapping repo_url (normalized) -> maturity.
"""
result: Dict[str, str] = {}
headers = _github_headers()
maturity_queries = {
"graduated": "topic:cncf-graduated",
"incubating": "topic:cncf-incubating",
"sandbox": "topic:cncf-sandbox",
}
async with httpx.AsyncClient(timeout=30.0) as client:
for maturity, query in maturity_queries.items():
try:
url = f"{GITHUB_API_BASE}/search/repositories?q={query}&per_page=100"
resp = await client.get(url, headers=headers)
resp.raise_for_status()
data = resp.json()
for repo in data.get("items", []):
repo_url = repo.get("html_url", "")
if repo_url:
result[_normalize_repo_url(repo_url)] = maturity
log_event(f" [CNCF] {maturity}: {len(data.get('items', []))} repos found")
except Exception as e:
log_event(f"[WARN] CNCF {maturity} search failed: {str(e)[:100]}")
await asyncio.sleep(GITHUB_RATE_DELAY)
log_event(f"[CNCF] Fetched {len(result)} projects from CNCF landscape")
return result
async def enrich_cncf_status(inventory: Dict) -> int:
"""Update inventory entries with cncf_status field.
If an entry maps to a CNCF-graduated project and lacks the
``[DE FACTO STANDARD]`` tag, the tag is appended.
Returns count of entries updated.
"""
landscape = await fetch_cncf_landscape()
if not landscape:
return 0
updated = 0
for url, entry in inventory.items():
if url.startswith("INTRO:"):
continue
normalized = _normalize_repo_url(url)
maturity = landscape.get(normalized)
if not maturity:
continue
old_status = entry.get("cncf_status")
entry["cncf_status"] = maturity
if old_status != maturity:
updated += 1
# Auto-tag graduated projects
if maturity == "graduated":
tags = entry.get("tags")
if isinstance(tags, list) and "[DE FACTO STANDARD]" not in tags:
tags.append("[DE FACTO STANDARD]")
entry["tags"] = tags
log_event(f"[CNCF] Enriched {updated} inventory entries with CNCF status")
return updated
# ---------------------------------------------------------------------------
# Module 2: Social Signal Enrichment (GitHub Activity)
# ---------------------------------------------------------------------------
async def _fetch_repo_activity(
client: httpx.AsyncClient,
owner_repo: str,
) -> Tuple[int, int]:
"""Fetch open issue + PR count for a single repo in one API call.
GitHub's open_issues_count includes PRs, so a single /repos endpoint
call is sufficient. Returns (open_issues_count, 0) the caller uses
the combined metric for health classification.
"""
headers = _github_headers()
open_issues = 0
try:
resp = await client.get(
f"{GITHUB_API_BASE}/repos/{owner_repo}",
headers=headers,
timeout=15.0,
)
if resp.status_code == 200:
data = resp.json()
open_issues = data.get("open_issues_count", 0)
elif resp.status_code == 429:
# Rate limited — back off
await asyncio.sleep(5.0)
except Exception as e:
log_event(f"[WARN] GitHub repo fetch failed for {owner_repo}: {str(e)[:120]}")
await asyncio.sleep(GITHUB_RATE_DELAY)
return open_issues, 0
def _classify_health(total_activity: int) -> str:
"""Classify community health based on combined issue + PR activity."""
if total_activity > HEALTH_ACTIVE:
return "active"
if total_activity >= HEALTH_HEALTHY:
return "healthy"
if total_activity > 0:
return "low"
return "dormant"
async def enrich_github_activity(inventory: Dict, max_repos: int = MAX_REPOS_DEFAULT) -> int:
"""Fetch recent issue/PR velocity for GitHub repos in the inventory.
Adds fields:
- ``gh_open_issues_30d``
- ``gh_open_prs_30d``
- ``gh_community_health`` ("active" | "healthy" | "low" | "dormant")
- ``gh_activity_checked`` (ISO timestamp)
Skips entries whose ``gh_activity_checked`` is less than 30 days old.
Processes at most *max_repos* repos per run.
Returns count of entries enriched.
"""
candidates: List[Tuple[str, str]] = [] # (url, owner/repo)
for url, entry in inventory.items():
if url.startswith("INTRO:"):
continue
repo = _parse_github_repo(url)
if not repo:
continue
if not _is_activity_stale(entry):
continue
candidates.append((url, repo))
if len(candidates) >= max_repos:
break
if not candidates:
log_event("[Activity] No GitHub repos require activity enrichment")
return 0
log_event(f"[Activity] Enriching {len(candidates)} GitHub repos (max {max_repos})")
enriched = 0
async with httpx.AsyncClient() as client:
for url, owner_repo in candidates:
try:
open_issues, open_prs = await _fetch_repo_activity(client, owner_repo)
total = open_issues + open_prs
health = _classify_health(total)
entry = inventory[url]
entry["gh_open_issues_30d"] = open_issues
entry["gh_open_prs_30d"] = open_prs
entry["gh_community_health"] = health
entry["gh_activity_checked"] = datetime.now(MADRID_TZ).isoformat()
enriched += 1
except Exception as e:
log_event(f"[WARN] Activity enrichment failed for {owner_repo}: {str(e)[:150]}")
log_event(f"[Activity] Enriched {enriched}/{len(candidates)} repos with community health data")
return enriched
# ---------------------------------------------------------------------------
# Module 3: License Change Detection
# ---------------------------------------------------------------------------
async def _fetch_current_license(
client: httpx.AsyncClient,
owner_repo: str,
) -> Optional[str]:
"""Fetch the current SPDX license identifier for a GitHub repo."""
headers = _github_headers()
try:
resp = await client.get(
f"{GITHUB_API_BASE}/repos/{owner_repo}",
headers=headers,
timeout=15.0,
)
if resp.status_code == 200:
data = resp.json()
lic = data.get("license")
if isinstance(lic, dict):
return lic.get("spdx_id", "N/A")
return "N/A"
elif resp.status_code == 404:
return None # repo not found / deleted
except Exception as e:
log_event(f"[WARN] License fetch failed for {owner_repo}: {str(e)[:120]}")
return None
async def detect_license_changes(inventory: Dict) -> List[Dict]:
"""Compare current gh_license with stored license, flag changes.
For each GitHub repo in inventory that has ``gh_license``:
- Fetches the current license from the GitHub API.
- Compares with the stored ``gh_license``.
- If different, updates the entry: sets ``gh_license`` to the new value
and stores the old value in ``gh_license_previous``.
Returns list of ``{url, old_license, new_license, title}`` dicts.
"""
candidates: List[Tuple[str, str, Dict]] = [] # (url, owner/repo, entry)
for url, entry in inventory.items():
if url.startswith("INTRO:"):
continue
if not entry.get("gh_license") or entry["gh_license"] == "N/A":
continue
repo = _parse_github_repo(url)
if not repo:
continue
candidates.append((url, repo, entry))
if not candidates:
log_event("[License] No repos with stored licenses to check")
return []
candidates = candidates[:MAX_REPOS_DEFAULT] # cap to same limit as activity enrichment
log_event(f"[License] Checking {len(candidates)} repos for license changes")
changes: List[Dict] = []
async with httpx.AsyncClient() as client:
for url, owner_repo, entry in candidates:
try:
current = await _fetch_current_license(client, owner_repo)
if current is None:
# Repo not found or API error — skip
continue
stored = entry.get("gh_license", "N/A")
if current != stored:
change_record = {
"url": url,
"old_license": stored,
"new_license": current,
"title": entry.get("title", url),
}
changes.append(change_record)
entry["gh_license_previous"] = stored
entry["gh_license"] = current
log_event(
f"[License] Change detected: {owner_repo} "
f"{stored} -> {current}"
)
except Exception as e:
log_event(f"[WARN] License check failed for {owner_repo}: {str(e)[:150]}")
await asyncio.sleep(GITHUB_RATE_DELAY)
log_event(f"[License] Detected {len(changes)} license change(s)")
return changes
# ---------------------------------------------------------------------------
# Full Enrichment Pipeline
# ---------------------------------------------------------------------------
async def run_enrichment():
"""Full enrichment pipeline: CNCF + GitHub activity + license detection."""
from src.inventory_manager import load_inventory, save_inventory
log_event("ENRICHMENT PIPELINE STARTING", section_break=True)
inventory = load_inventory()
log_event(f"[*] Loaded {len(inventory)} inventory entries")
cncf_count = await enrich_cncf_status(inventory)
activity_count = await enrich_github_activity(inventory)
license_changes = await detect_license_changes(inventory)
save_inventory(inventory)
log_event(
f"Enrichment complete: {cncf_count} CNCF, "
f"{activity_count} activity, "
f"{len(license_changes)} license changes",
section_break=True,
)
return license_changes
if __name__ == "__main__":
asyncio.run(run_enrichment())
+3 -2
View File
@@ -94,10 +94,11 @@ async def fetch_github_metadata(client: httpx.AsyncClient, url: str, sem: asynci
return url, default_meta
from src.inventory_manager import load_inventory
async def run_enrichment():
print("[*] Loading inventory database...")
with open(INVENTORY_PATH, "r") as f:
inventory = yaml.safe_load(f) or {}
inventory = load_inventory()
print(f"[*] Loaded {len(inventory)} total entries.")
+20 -9
View File
@@ -122,7 +122,8 @@ async def discover_optimal_models():
if name not in all_supported: all_supported.append(name)
elif resp.status_code == 429:
log_event(f" [!] Discovery Key is rate-limited (429). Skipping.")
except: pass
except Exception as e:
log_event(f"[WARN] model discovery for key: {str(e)[:100]}")
if not all_supported:
log_event(" [!] Discovery failed. Falling back to safe defaults.")
@@ -136,7 +137,8 @@ async def discover_optimal_models():
try:
version = float(version_match.group(1))
score += version * 50
except: pass
except Exception as e:
log_event(f"[WARN] parse model version for {name}: {str(e)[:100]}")
if "-ultra" in name: score += 100
elif "-pro" in name: score += 50
elif "-flash" in name: score += 25
@@ -169,7 +171,9 @@ class GeminiDiagnostics:
async def resolve_url(url: str) -> str:
shorteners = ['t.co', 'bit.ly', 'buff.ly', 'goo.gl', 'tinyurl.com', 't.ly', 'rb.gy', 'is.gd', 'drp.li', 't.me', 'lnkd.in']
try: domain = url.split("//")[-1].split("/")[0].lower()
except: return url
except Exception as e:
log_event(f"[WARN] parse domain from URL {url[:50]}: {str(e)[:100]}")
return url
final_url, max_hops, current_hop = url, 5, 0
async with httpx.AsyncClient(follow_redirects=True, timeout=8) as client:
while current_hop < max_hops:
@@ -180,7 +184,9 @@ async def resolve_url(url: str) -> str:
new_url = str(resp.url)
if new_url == final_url: break
final_url, current_hop = new_url, current_hop + 1
except: break
except Exception as e:
log_event(f"[WARN] resolve URL hop for {final_url[:50]}: {str(e)[:100]}")
break
# Mandate 34: Prevent multiple trailing slashes using centralized utility
return sanitize_trailing_slashes(final_url)
@@ -217,7 +223,8 @@ async def get_github_activity(url: str) -> Dict:
try:
from src.config import GH_TOKEN
headers = {"Authorization": f"token {GH_TOKEN}"} if GH_TOKEN else {}
except:
except Exception as e:
log_event(f"[WARN] import GH_TOKEN for GitHub activity: {str(e)[:100]}")
headers = {}
try:
@@ -232,7 +239,8 @@ async def get_github_activity(url: str) -> Dict:
"gh_pushed": data.get("pushed_at", "N/A"),
"gh_license": lic_id
}
except: pass
except Exception as e:
log_event(f"[WARN] fetch GitHub activity for {url}: {str(e)[:100]}")
return default_meta
@@ -436,7 +444,8 @@ async def call_gemini_with_retry(prompt: str, response_format: str = "json", max
resp_json = {}
try: resp_json = response.json()
except: pass
except Exception as e:
log_event(f"[WARN] parse Gemini response JSON: {str(e)[:100]}")
usage = resp_json.get("usageMetadata", {})
SESSION_TRACKER.track_call(current_idx, model, response.status_code, usage, role=role)
@@ -452,7 +461,8 @@ async def call_gemini_with_retry(prompt: str, response_format: str = "json", max
try:
data = json.loads(match.group(0))
return data
except: pass
except Exception as e:
log_event(f"[WARN] parse Gemini content JSON for model {model}: {str(e)[:100]}")
# QUALITY UPGRADE: If flash failed parsing, don't give up on the key, try a Pro model
if ("flash" in model or "lite" in model) and any("pro" in m for m in models):
@@ -574,7 +584,8 @@ async def fetch_youtube_metadata(url: str) -> Optional[Dict]:
try:
transcript = YouTubeTranscriptApi.get_transcript(vid, languages=['en', 'es'])
transcript_text = " ".join([t['text'] for t in transcript[:100]])
except: pass
except Exception as e:
log_event(f"[WARN] fetch YouTube transcript for {vid}: {str(e)[:100]}")
full_description = f"{description}\n\n[Transcript Snippet]: {transcript_text}" if transcript_text else description
+6 -3
View File
@@ -21,14 +21,16 @@ class RepositoryController:
try:
file_meta = self.repository.get_contents(file_path, ref=branch_name)
return base64.b64decode(file_meta.content).decode("utf-8")
except:
except Exception as e:
print(f"[WARN] Failed to get file '{file_path}' from branch '{branch_name}': {str(e)[:100]}")
return ""
def apply_historical_chunk(self, updates: dict, next_since: str) -> None:
branch_name = "bot/historical-accumulator"
try:
self.repository.get_branch(branch_name)
except:
except Exception as e:
print(f"[WARN] Branch '{branch_name}' not found, creating: {str(e)[:100]}")
self._create_feature_branch(branch_name)
for file_path, content in updates.items():
@@ -54,7 +56,8 @@ class RepositoryController:
try:
self._create_feature_branch(branch_name)
except:
except Exception as e:
print(f"[WARN] Branch creation failed, retrying with unique suffix: {str(e)[:100]}")
branch_name = f"bot/knowledge-update-{timestamp_slug}-{id(updates)}"
self._create_feature_branch(branch_name)
+4 -3
View File
@@ -88,11 +88,12 @@ class BackupDataExtractor:
def parse_date(x):
try:
return datetime.strptime(x["timestamp"], '%a %b %d %H:%M:%S +0000 %Y')
except:
except Exception as e:
print(f"[WARN] Date parse failed for timestamp: {str(e)[:100]}")
return datetime.min
results.sort(key=parse_date)
except:
pass
except Exception as e:
print(f"[WARN] Sorting results by date failed: {str(e)[:100]}")
self.log_audit("Backup Ingestion", True, f"Total links extracted: {len(results)}")
return results
+6 -4
View File
@@ -52,8 +52,8 @@ class SocialDataExtractor:
try:
from playwright.async_api import async_playwright
import playwright_stealth
except:
self.log_audit("Playwright", False, "Libraries not available.")
except Exception as e:
self.log_audit("Playwright", False, f"Libraries not available: {str(e)[:100]}")
return []
collected_tweets = {}
@@ -74,14 +74,16 @@ class SocialDataExtractor:
for k in ['sameSite', 'storeId', 'id']: c.pop(k, None)
formatted.append(c)
await context.add_cookies(formatted)
except: pass
except Exception as e:
log_event(f"[WARN] Failed to load Twitter cookies: {str(e)[:100]}")
for account in accounts:
page = await context.new_page()
try:
if hasattr(playwright_stealth, 'stealth_async'): await playwright_stealth.stealth_async(page)
elif hasattr(playwright_stealth, 'stealth'): playwright_stealth.stealth(page)
except: pass
except Exception as e:
log_event(f"[WARN] Playwright stealth setup failed: {str(e)[:100]}")
if strategy == "search":
import urllib.parse
+13 -6
View File
@@ -44,7 +44,8 @@ class IntelligentLinkCleaner:
def _load_memory(self) -> Dict:
if os.path.exists(MEMORY_FILE):
try: return json.load(open(MEMORY_FILE, 'r'))
except: pass
except Exception as e:
log_event(f"[WARN] load health learning memory: {str(e)[:100]}")
return {"domains": {}, "known_soft_404_patterns": []}
def _save_memory(self):
@@ -198,8 +199,10 @@ class IntelligentLinkCleaner:
else:
log_event(f" [✨] RESCUED: {u} -> {new_loc}")
check_results[u] = (True, "resurrected", new_loc)
except: pass
except: pass
except Exception as e:
log_event(f"[WARN] verify rescued URL {new_loc[:50]}: {str(e)[:100]}")
except Exception as e:
log_event(f"[WARN] AI rescue batch: {str(e)[:100]}")
# 2.8. Finalize Status
log_event("FINALIZING STATUS AND METRICS...", section_break=True)
@@ -309,7 +312,8 @@ class IntelligentLinkCleaner:
log_event(f" [⚖️] LICENSE ALERT: {url} -> {new_lic}")
entry["status"] = "review_required"
entry["gh_license"] = new_lic
except: pass
except Exception as e:
log_event(f"[WARN] license guard check for {url}: {str(e)[:100]}")
if final_url != url:
u_p = url.split("://")[-1].rstrip("/"); f_p = final_url.split("://")[-1].rstrip("/")
@@ -325,12 +329,15 @@ class IntelligentLinkCleaner:
h = url.replace("/master/", "/main/")
try:
if (await client.get(h)).status_code < 400: return True, "healed", h
except: pass
except Exception as e:
log_event(f"[WARN] heal master->main for {url}: {str(e)[:100]}")
m = re.search(r'(https?://github\.com/[^/]+/[^/]+)', url)
if m and (await client.get(m.group(1))).status_code < 400: return True, "consolidated", m.group(1)
return False, "404", None
return True, f"Soft Block {resp.status_code}", None
except: return True, "Error", None
except Exception as e:
log_event(f"[WARN] URL check logic for {url}: {str(e)[:100]}")
return True, "Error", None
async def prune_orphaned_metadata(self):
valid_map = {}
+169 -17
View File
@@ -1,46 +1,198 @@
import os
import json
import sqlite3
import yaml
from typing import Dict
INVENTORY_PATH = "data/inventory.yaml"
SQL_PATH = "data/inventory.sql"
try:
from yaml import CSafeLoader as Loader, CSafeDumper as Dumper
except ImportError:
from yaml import SafeLoader as Loader, SafeDumper as Dumper
def load_inventory(shard_file: str = None) -> Dict:
"""
Loads the entire inventory from a single YAML file.
Legacy/Fast-Track standard restored: Zero-sharding complexity.
Loads the entire inventory.
Option 3: Imports inventory.sql to temporary SQLite database in-memory,
queries the database to reconstruct the Python dictionary, and returns it.
Falls back to inventory.yaml if SQL file is not present.
"""
if os.path.exists(INVENTORY_PATH):
if not os.path.exists(SQL_PATH) and os.path.exists(INVENTORY_PATH):
try:
with open(INVENTORY_PATH, "r") as file:
return yaml.safe_load(file) or {}
except: pass
return {}
with open(INVENTORY_PATH, "r", encoding="utf-8") as file:
return yaml.load(file, Loader=Loader) or {}
except Exception as e:
pass
return {}
if not os.path.exists(SQL_PATH):
return {}
conn = sqlite3.connect(":memory:")
try:
with open(SQL_PATH, "r", encoding="utf-8") as f:
conn.executescript(f.read())
except Exception as e:
conn.close()
# Fallback to YAML if SQL import fails
if os.path.exists(INVENTORY_PATH):
try:
with open(INVENTORY_PATH, "r", encoding="utf-8") as file:
return yaml.load(file, Loader=Loader) or {}
except Exception as e:
print(f"[WARN] YAML fallback load failed: {str(e)[:100]}")
return {}
cursor = conn.cursor()
try:
cursor.execute("SELECT * FROM resources")
rows = cursor.fetchall()
col_names = [description[0] for description in cursor.description]
except Exception as e:
conn.close()
return {}
inv = {}
for row in rows:
record = dict(zip(col_names, row))
url = record.pop("url")
# Deserialize JSON lists/dicts
for json_field in ["hierarchy", "tags", "v1_locations", "v2_locations", "youtube_mosaic", "extra_metadata"]:
val = record.get(json_field)
if val:
try:
record[json_field] = json.loads(val)
except Exception as e:
print(f"[WARN] JSON parse failed for field '{json_field}': {str(e)[:100]}")
record[json_field] = [] if json_field not in ["youtube_mosaic", "extra_metadata"] else {}
else:
record[json_field] = [] if json_field not in ["youtube_mosaic", "extra_metadata"] else {}
# Merge extra_metadata keys back into the record dictionary
extra = record.pop("extra_metadata", {})
if isinstance(extra, dict):
record.update(extra)
# Restore types
if record.get("is_microservice") is not None:
record["is_microservice"] = bool(record["is_microservice"])
if record.get("needs_ai_refresh") is not None:
record["needs_ai_refresh"] = bool(record["needs_ai_refresh"])
inv[url] = record
conn.close()
return inv
def save_inventory(inv: Dict, shard_file: str = None):
"""
Saves the entire inventory to a single YAML file.
Saves the entire inventory.
Option 3: Creates an in-memory SQLite table, populates it,
and exports it back to inventory.sql.
Also dual-saves a backup to inventory.yaml using fast CDumper.
"""
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS resources (
url TEXT PRIMARY KEY,
title TEXT,
description TEXT,
year TEXT,
stars INTEGER,
ai_summary TEXT,
language TEXT,
resource_type TEXT,
complexity TEXT,
is_microservice BOOLEAN,
status TEXT,
addition_method TEXT,
content_hash TEXT,
health_score REAL,
last_checked REAL,
needs_ai_refresh BOOLEAN,
discovered_at TEXT,
last_ai_eval TEXT,
company TEXT,
geo_region TEXT,
hierarchy TEXT,
tags TEXT,
v1_locations TEXT,
v2_locations TEXT,
youtube_mosaic TEXT,
extra_metadata TEXT
);
""")
columns = [
"url", "title", "description", "year", "stars", "ai_summary", "language",
"resource_type", "complexity", "is_microservice", "status", "addition_method",
"content_hash", "health_score", "last_checked", "needs_ai_refresh",
"discovered_at", "last_ai_eval", "company", "geo_region",
"hierarchy", "tags", "v1_locations", "v2_locations", "youtube_mosaic", "extra_metadata"
]
for url, entry in inv.items():
if not isinstance(entry, dict):
continue
record = {col: entry.get(col) for col in columns if col not in ["hierarchy", "tags", "v1_locations", "v2_locations", "youtube_mosaic", "extra_metadata"]}
record["url"] = url
# Serialize lists/dicts
record["hierarchy"] = json.dumps(entry.get("hierarchy", []))
record["tags"] = json.dumps(entry.get("tags", []))
record["v1_locations"] = json.dumps(entry.get("v1_locations", []))
record["v2_locations"] = json.dumps(entry.get("v2_locations", []))
record["youtube_mosaic"] = json.dumps(entry.get("youtube_mosaic", {}))
# Pull arbitrary extra fields
extra = {}
for k, v in entry.items():
if k not in columns:
extra[k] = v
record["extra_metadata"] = json.dumps(extra)
# Conversions
record["is_microservice"] = 1 if record.get("is_microservice") else 0
record["needs_ai_refresh"] = 1 if record.get("needs_ai_refresh") else 0
placeholders = ", ".join(["?"] * len(columns))
values = [record[col] for col in columns]
cursor.execute(f"INSERT OR REPLACE INTO resources ({', '.join(columns)}) VALUES ({placeholders})", values)
conn.commit()
# Dump to SQL
os.makedirs(os.path.dirname(SQL_PATH), exist_ok=True)
with open(SQL_PATH, "w", encoding="utf-8") as f:
for line in conn.iterdump():
f.write(f"{line}\n")
conn.close()
# Dual-Save to YAML (Fast C-Dumper)
os.makedirs(os.path.dirname(INVENTORY_PATH), exist_ok=True)
with open(INVENTORY_PATH, "w") as file:
yaml.dump(inv, file, sort_keys=False, allow_unicode=True)
with open(INVENTORY_PATH, "w", encoding="utf-8") as file:
yaml.dump(inv, file, Dumper=Dumper, sort_keys=False, allow_unicode=True)
def get_shard_name(url: str) -> str:
# Kept for backward compatibility but unused in single-file mode
return "inventory.yaml"
def update_inventory_entry(inventory: Dict, norm_url: str, new_data: Dict):
"""
Updates an inventory entry by merging new_data with existing data,
preserving metadata keys like 'youtube_mosaic' if they are not in new_data.
"""
if norm_url not in inventory:
inventory[norm_url] = {}
existing = inventory[norm_url]
if isinstance(existing, dict):
merged = existing.copy()
existing_discovered = existing.get("discovered_at")
merged.update(new_data)
if existing_discovered:
merged["discovered_at"] = existing_discovered
inventory[norm_url] = merged
else:
inventory[norm_url] = new_data
+2 -2
View File
@@ -32,5 +32,5 @@ def _write_to_file(message: str):
os.makedirs(os.path.dirname(DEFAULT_LOG_PATH), exist_ok=True)
with open(DEFAULT_LOG_PATH, "a") as f:
f.write(message + "\n")
except:
pass
except Exception as e:
print(f"[WARN] write to log file: {str(e)[:100]}")
+34 -20
View File
@@ -4,6 +4,10 @@ import os
import json
import re
import yaml
try:
from yaml import CSafeLoader as Loader
except ImportError:
from yaml import SafeLoader as Loader
import httpx
from urllib.parse import urlparse
from datetime import datetime, timedelta
@@ -55,7 +59,8 @@ async def master_orchestrator():
since_date = until_date - timedelta(days=days)
log_event(f"[*] Mode: Relative range (Last {days} days) -> {since_date.date()}")
is_historical = False # Force normal mode for relative range
except:
except Exception as e:
log_event(f"[WARN] parse CURATION_DAYS_BACK: {str(e)[:100]}")
since_date = get_last_date()
elif is_historical:
# DEFAULT START DATE: 2026-05-15 (as requested)
@@ -82,7 +87,8 @@ async def master_orchestrator():
try:
since_date = datetime.fromisoformat(env_start).replace(tzinfo=MADRID_TZ)
log_event(f"[*] Normal Mode: From manual workflow date {since_date.date()}")
except:
except Exception as e:
log_event(f"[WARN] parse CURATION_START_DATE: {str(e)[:100]}")
since_date = get_last_date()
log_event(f"[*] Normal Mode: Error parsing manual date, using state.json {since_date.date()}")
else:
@@ -114,8 +120,8 @@ async def master_orchestrator():
if os.path.exists(sources_file):
try:
with open(sources_file, 'r') as f:
data = yaml.safe_load(f)
with open(sources_file, 'r', encoding='utf-8') as f:
data = yaml.load(f, Loader=Loader)
all_accounts = set()
for topic_data in data.get("sources", []):
topic_name = topic_data.get("topic")
@@ -206,11 +212,12 @@ async def master_orchestrator():
parsed = urlparse(expanded_url)
domain = parsed.netloc.lower()
domain_info = health_learning["domains"].setdefault(domain, {"attempts": 0, "failures": 0, "consecutive_failures": 0})
domain_info = health_learning["domains"].setdefault(domain, {"attempts": 0, "failures": 0, "consecutive_failures": 0, "success_rate": 100.0})
consecutive_failures = domain_info.get("consecutive_failures", 0)
success_rate = domain_info.get("success_rate", 100.0)
timeout_val = 12.0
if consecutive_failures >= 3:
if consecutive_failures >= 3 or success_rate < 50.0:
timeout_val = 3.0
ua = fallback_user_agents[idx % len(fallback_user_agents)]
else:
@@ -226,18 +233,21 @@ async def master_orchestrator():
resp = await client.get(expanded_url)
if resp.status_code == 404:
asset["health"] = "dead" # Definitively dead
info = health_learning["domains"][domain]
info["failures"] = info.get("failures", 0) + 1
info["consecutive_failures"] = info.get("consecutive_failures", 0) + 1
domain_info["failures"] = domain_info.get("failures", 0) + 1
domain_info["consecutive_failures"] = domain_info.get("consecutive_failures", 0) + 1
else:
asset["health"] = "online"
info = health_learning["domains"][domain]
info["consecutive_failures"] = 0
except:
domain_info["consecutive_failures"] = 0
except Exception as e:
log_event(f"[WARN] health check for {expanded_url}: {str(e)[:100]}")
asset["health"] = "timeout" # Assume alive but unreachable for now
info = health_learning["domains"][domain]
info["failures"] = info.get("failures", 0) + 1
info["consecutive_failures"] = info.get("consecutive_failures", 0) + 1
domain_info["failures"] = domain_info.get("failures", 0) + 1
domain_info["consecutive_failures"] = domain_info.get("consecutive_failures", 0) + 1
# Recalculate success rate and store it
attempts = domain_info.get("attempts", 1)
failures = domain_info.get("failures", 0)
domain_info["success_rate"] = round(((attempts - failures) / attempts) * 100.0, 2)
# 3. GitHub Metadata Enrichment
if "github.com" in expanded_url:
@@ -254,7 +264,8 @@ async def master_orchestrator():
gh_data = gh_resp.json()
asset["gh_stars"] = gh_data.get("stargazers_count")
asset["gh_updated"] = gh_data.get("updated_at", "").split("T")[0]
except: pass
except Exception as e:
log_event(f"[WARN] GitHub metadata enrichment for {expanded_url}: {str(e)[:100]}")
return asset
@@ -291,7 +302,8 @@ async def master_orchestrator():
found = re.findall(r'\]\((https?://[^\)]+)\)', content)
for url in found:
existing_urls.add(url.split('#')[0].rstrip('/').lower())
except: pass
except Exception as e:
log_event(f"[WARN] read docs/{file} for URL extraction: {str(e)[:100]}")
log_event(f"[*] Global Deduplication: {len(existing_urls)} existing URLs loaded.")
@@ -329,12 +341,14 @@ async def master_orchestrator():
if isinstance(ts, str):
try:
asset_date = datetime.strptime(ts, '%a %b %d %H:%M:%S +0000 %Y').replace(tzinfo=MADRID_TZ)
except:
except Exception as e:
try: asset_date = datetime.fromisoformat(ts.replace('Z', '+00:00'))
except: pass
except Exception as e2:
log_event(f"[WARN] parse timestamp '{ts[:30]}': {str(e2)[:100]}")
if asset_date and asset_date > max_tweet_date:
max_tweet_date = asset_date
except: pass
except Exception as e:
log_event(f"[WARN] process asset timestamp: {str(e)[:100]}")
assets_to_evaluate.append(asset)
+3 -1
View File
@@ -66,7 +66,9 @@ def get_system_mandates() -> str:
if os.path.exists(MANDATES_JSON):
try:
return json.load(open(MANDATES_JSON, "r")).get("system_snippet", "")
except: return ""
except Exception as e:
log_event(f"[WARN] Failed to load system mandates: {str(e)[:100]}")
return ""
return ""
if __name__ == "__main__":
+445
View File
@@ -0,0 +1,445 @@
from __future__ import annotations
import os
import json
import asyncio
from datetime import datetime, timedelta
from typing import Dict, List, Any
from src.inventory_manager import load_inventory
from src.gemini_utils import call_gemini_with_retry
from src.config import MADRID_TZ
from src.logger import log_event
DIGEST_OUTPUT_PATH = "data/news_digest.json"
class NewsDigestEngine:
"""Generates a curated news digest by filtering inventory entries by
recency and using Gemini AI to rank the most relevant ones per category.
Three time windows (3 / 6 / 12 months) are produced in a single run.
Each window contains up to 10 AI-ranked items per digest category.
"""
# ------------------------------------------------------------------ #
# 26 Digest Categories mapped from V2 category slugs #
# ------------------------------------------------------------------ #
DIGEST_CATEGORIES: Dict[str, List[str]] = {
# --- TECH CORE (9) ---
"Kubernetes & Orchestration": [
"kubernetes", "kubernetes-tools", "kubernetes-tutorials",
"kubectl-commands", "kubernetes-releases", "kubernetes-autoscaling",
"kubernetes-operators-controllers", "kubernetes-based-devel",
"kubernetes-alternatives", "kubernetes-client-libraries",
"kubernetes-bigdata", "managed-kubernetes-in-public-cloud", "helm",
],
"Containers & Runtime": [
"docker", "container-managers", "serverless", "noops", "registries",
],
"Networking & Service Mesh": [
"networking", "kubernetes-networking", "servicemesh", "istio",
"caching", "web-servers", "cloudflare",
],
"Architecture & Microservices": [
"introduction", "faq", "cloud-arch-diagrams", "matrix-table",
"other-awesome-lists", "about",
],
"Data, Messaging & Storage": [
"databases", "nosql", "newsql", "message-queue", "crunchydata",
"yaml", "kubernetes-storage", "kubernetes-backup-migrations",
],
"AI & Agents": [
"ai", "ai-agents-mcp", "chatgpt",
],
"MLOps & Data Science": [
"mlops",
],
"Python, Java & Developer Ecosystem": [
"python", "golang", "java_frameworks", "java_app_servers",
"java-and-java-performance-optimization", "javascript", "dotnet",
"angular", "react", "web3", "api",
"swagger-code-generator-for-rest-apis", "postman",
"lowcode-nocode", "devel-sites", "dom", "linux-dev-env",
"ChromeDevTools", "xamarin", "jvm-parameters-matrix-table",
"maven-gradle", "embedded-servlet-containers", "visual-studio",
],
"Linux & System Foundations": [
"linux", "git",
],
# --- PLATFORM & OPS (8) ---
"Security & Compliance": [
"securityascode", "kubernetes-security", "aws-security", "oauth",
"devsecops",
],
"Infrastructure as Code": [
"iac", "terraform", "pulumi", "crossplane", "ansible",
"kustomize", "chef", "liquibase",
],
"CI/CD & GitOps": [
"cicd", "gitops", "argo", "flux", "tekton", "jenkins",
"jenkins-alternatives", "sonarqube", "cicd-kubernetes-plugins",
"openshift-pipelines", "stackstorm", "keptn",
],
"Observability, SRE & Testing": [
"sre", "monitoring", "prometheus", "grafana",
"kubernetes-monitoring", "chaos-engineering", "qa",
"test-automation-frameworks", "testops",
"performance-testing-with-jenkins-and-jmeter",
"kubernetes-troubleshooting",
],
"DevOps & Culture": [
"devops", "devops-tools", "project-management-methodology",
"project-management-tools",
],
"Platform Engineering & DevEx": [
"developerportals", "scaffolding", "mkdocs",
],
"FinOps & Cloud Cost": [
"finops", "aws-pricing",
],
"Certification & Training": [
"elearning", "interview-questions", "aws-training", "cheatsheets",
"demos",
],
# --- CLOUD & ENTERPRISE (5) ---
"AWS": [
"aws", "aws-architecture", "aws-security", "aws-networking",
"aws-databases", "aws-storage", "aws-monitoring", "aws-iac",
"aws-tools-scripts", "aws-messaging", "aws-data", "aws-devops",
"aws-serverless", "aws-containers", "aws-backup",
"aws-newfeatures", "aws-miscellaneous", "aws-spain",
],
"Azure": [
"azure",
],
"GCP, OCI & Others": [
"GoogleCloudPlatform", "ibm_cloud", "oraclecloud",
"digitalocean", "scaleway", "edge-computing",
"public-cloud-solutions",
],
"OpenShift / Red Hat": [
"openshift", "ocp3", "ocp4", "openshift-pipelines", "rancher",
],
"Virtualization & Private Cloud": [
"kubernetes-on-premise", "kubernetes-alternatives",
"private-cloud-solutions",
],
# --- INDUSTRY / GEO (4) resolved via geo_region, not slugs ---
"Americas": [],
"Europe": [],
"España": [],
"Asia-Pacific": [],
}
GEO_CATEGORIES: Dict[str, str] = {
"Americas": "americas",
"Europe": "europe",
"España": "spain",
"Asia-Pacific": "asia_pacific",
}
PERIODS: Dict[str, int] = {
"3_months": 90,
"6_months": 180,
"12_months": 365,
}
ITEMS_PER_PERIOD: Dict[str, int] = {
"3_months": 10,
"6_months": 15,
"12_months": 20,
}
# ------------------------------------------------------------------ #
def __init__(self) -> None:
self.inventory: Dict[str, Any] = load_inventory()
# Reverse map: v2_category_slug -> digest_category_name
self.category_map: Dict[str, str] = {}
for digest_cat, slugs in self.DIGEST_CATEGORIES.items():
for slug in slugs:
self.category_map[slug] = digest_cat
# ------------------------------------------------------------------ #
# Classification helpers #
# ------------------------------------------------------------------ #
def _get_entry_category(self, entry: dict) -> str | None:
"""Determine which digest category an entry belongs to.
Checks ``v2_locations`` paths first (more specific), then falls
back to the ``category`` field.
"""
for loc in entry.get("v2_locations", []):
slug = loc.replace(".md", "")
if slug in self.category_map:
return self.category_map[slug]
cat = entry.get("category", "")
if cat in self.category_map:
return self.category_map[cat]
return None
def _get_entry_geo(self, entry: dict) -> str | None:
"""Return the geo digest category using geo_region field, falling back to URL TLD inference."""
region = entry.get("geo_region", "")
for geo_name, geo_val in self.GEO_CATEGORIES.items():
if region == geo_val:
return geo_name
# Fallback: infer from URL TLD
return self._infer_geo_from_url(entry.get("url", ""))
@staticmethod
def _infer_geo_from_url(url: str) -> str | None:
"""Infer geo category from URL TLD. Returns GEO_CATEGORIES key or None."""
try:
from urllib.parse import urlparse
host = urlparse(url).hostname or ""
# Ordered longest-first to avoid .uk matching before .co.uk
tld_to_region = [
(".com.au", "Asia-Pacific"), (".co.uk", "Europe"), (".co.jp", "Asia-Pacific"),
(".co.kr", "Asia-Pacific"), (".com.br", "Americas"), (".com.mx", "Americas"),
(".es", "España"), (".de", "Europe"), (".fr", "Europe"), (".it", "Europe"),
(".pt", "Europe"), (".nl", "Europe"), (".be", "Europe"), (".se", "Europe"),
(".dk", "Europe"), (".fi", "Europe"), (".no", "Europe"), (".ch", "Europe"),
(".at", "Europe"), (".pl", "Europe"), (".cz", "Europe"), (".uk", "Europe"),
(".ie", "Europe"), (".eu", "Europe"), (".cn", "Asia-Pacific"),
(".jp", "Asia-Pacific"), (".kr", "Asia-Pacific"), (".sg", "Asia-Pacific"),
(".in", "Asia-Pacific"), (".au", "Asia-Pacific"), (".nz", "Asia-Pacific"),
(".ca", "Americas"), (".mx", "Americas"), (".br", "Americas"),
]
for tld, region in tld_to_region:
if host.endswith(tld):
return region
except Exception:
pass
return None
@staticmethod
def _is_within_period(entry: dict, cutoff_iso: str) -> bool:
"""Check if entry falls within the time period using discovered_at,
with year field as fallback for backfilled entries."""
discovered = entry.get("discovered_at", "")
if discovered:
try:
if discovered >= cutoff_iso:
return True
except Exception:
pass
year = entry.get("year", "")
if year and isinstance(year, str) and year.isdigit():
cutoff_year = cutoff_iso[:4] if len(cutoff_iso) >= 4 else "2020"
return year >= cutoff_year
return False
# ------------------------------------------------------------------ #
# Prompt builder #
# ------------------------------------------------------------------ #
@staticmethod
def _build_ranking_prompt(
category: str, entries: List[dict], period: str
) -> str:
"""Assemble the Gemini prompt that asks for a ranked TOP-10."""
period_label = period.replace("_", " ")
lines: List[str] = []
for i, e in enumerate(entries[:50]):
summary_fragment = (e.get("ai_summary", "") or "")[:200]
lines.append(
f'{i}. "{e.get("title", "Unknown")}" '
f'({e.get("url", "")}) | '
f'Stars: {e.get("stars", 0)} | '
f'Year: {e.get("year", "N/A")} | '
f'Summary: {summary_fragment}'
)
entries_text = "\n".join(lines)
return (
"You are a Senior Technical Curator for a Cloud Native "
"knowledge portal.\n"
f'Given these resources discovered in the last {period_label} '
f'for "{category}", select the TOP 10 most relevant.\n\n'
"SCORING CRITERIA:\n"
"- Industry Impact (30%): Does this change how teams "
"build/operate?\n"
"- Technical Novelty (25%): New capability, paradigm shift, "
"major release?\n"
"- Enterprise Adoption (20%): GA releases, production-ready?\n"
"- Community Signal (15%): CNCF graduations, major blog posts?\n"
"- Nubenetes Relevance (10%): Directly related to cloud native?\n\n"
'Respond ONLY JSON: {"items": [{"idx": int, '
'"impact": "critical|high|medium", '
'"why": "1 sentence explaining why this matters"}]}\n\n'
f"RESOURCES:\n{entries_text}"
)
# ------------------------------------------------------------------ #
# Star-based fallback (used when Gemini is unavailable / < 3 entries) #
# ------------------------------------------------------------------ #
@staticmethod
def _fallback_items(
entries: List[dict], cat_name: str, limit: int = 10
) -> List[dict]:
"""Return up to *limit* items using a deterministic star-based
ranking (no AI call required)."""
return [
{
"url": e["url"],
"title": e.get("title", "Unknown"),
"date": e.get("discovered_at", "")[:10],
"stars": e.get("stars") or 0,
"impact": "high" if (e.get("stars") or 0) >= 4 else "medium",
"why": (e.get("ai_summary", "") or "")[:200],
"category": cat_name,
}
for e in entries[:limit]
]
# ------------------------------------------------------------------ #
# Core generation loop #
# ------------------------------------------------------------------ #
async def generate_digest(self) -> dict:
"""Generate the full digest for all categories and time periods.
Returns a nested dict::
{
"3_months": { "Kubernetes & Orchestration": [...], ... },
"6_months": { ... },
"12_months": { ... }
}
"""
digest: Dict[str, Dict[str, List[dict]]] = {}
for period_name, days in self.PERIODS.items():
cutoff = (
datetime.now(MADRID_TZ) - timedelta(days=days)
).isoformat()
digest[period_name] = {}
# Bucket entries into their digest categories
category_pools: Dict[str, List[dict]] = {}
for url, entry in self.inventory.items():
if not isinstance(entry, dict):
continue
if not self._is_within_period(entry, cutoff):
continue
# Tech / topic category
cat = self._get_entry_category(entry)
if cat:
category_pools.setdefault(cat, []).append(
dict(entry, url=url)
)
# Geo category (entry may belong to both)
geo = self._get_entry_geo(entry)
if geo:
category_pools.setdefault(geo, []).append(
dict(entry, url=url)
)
# Rank each category pool
for cat_name, entries in category_pools.items():
entries.sort(
key=lambda x: (
x.get("stars") or 0,
x.get("discovered_at") or "",
),
reverse=True,
)
max_items = self.ITEMS_PER_PERIOD.get(period_name, 10)
if len(entries) < 3:
digest[period_name][cat_name] = self._fallback_items(
entries, cat_name, limit=max_items
)
continue
try:
prompt = self._build_ranking_prompt(
cat_name, entries, period_name
)
result = await call_gemini_with_retry(
prompt,
prefer_flash=True,
role="Digest-Analyst",
)
ranked: List[dict] = []
for item in result.get("items", []):
idx = int(item.get("idx", -1))
if 0 <= idx < len(entries):
e = entries[idx]
ranked.append(
{
"url": e["url"],
"title": e.get("title", "Unknown"),
"date": e.get("discovered_at", "")[:10],
"stars": e.get("stars", 0),
"impact": item.get("impact", "medium"),
"why": item.get("why", ""),
"category": cat_name,
}
)
digest[period_name][cat_name] = ranked[:max_items]
log_event(
f" [Digest] {period_name}/{cat_name}: "
f"{len(ranked)} items ranked"
)
except Exception as exc:
log_event(
f" [Digest WARN] {period_name}/{cat_name}: "
f"Gemini failed ({str(exc)[:80]}), "
"using star-based fallback"
)
digest[period_name][cat_name] = self._fallback_items(
entries, cat_name, limit=max_items
)
# Respect Gemini rate limits
await asyncio.sleep(1.0)
return digest
# ------------------------------------------------------------------ #
# Persistence #
# ------------------------------------------------------------------ #
@staticmethod
def save_digest(digest: dict) -> None:
"""Serialise *digest* to ``data/news_digest.json``."""
os.makedirs(os.path.dirname(DIGEST_OUTPUT_PATH), exist_ok=True)
with open(DIGEST_OUTPUT_PATH, "w", encoding="utf-8") as fh:
json.dump(digest, fh, indent=2, ensure_ascii=False)
log_event(f"[Digest] Saved to {DIGEST_OUTPUT_PATH}")
# ====================================================================== #
# CLI / CI entry point #
# ====================================================================== #
async def run_news_digest() -> None:
"""Entry point for the CI pipeline."""
log_event("STARTING NEWS DIGEST GENERATION", section_break=True)
engine = NewsDigestEngine()
digest = await engine.generate_digest()
engine.save_digest(digest)
total_items = sum(
len(items) for period in digest.values() for items in period.values()
)
log_event(
f"NEWS DIGEST COMPLETE: {total_items} total items across all periods"
)
if __name__ == "__main__":
asyncio.run(run_news_digest())
+2 -1
View File
@@ -47,7 +47,8 @@ def auto_format_file(filepath: str):
try:
cleaned = normalize_url(url)
return f"[{text}]({cleaned})"
except:
except Exception as e:
print(f"[WARN] URL normalization failed for '{url[:60]}': {str(e)[:100]}")
return match.group(0)
return match.group(0)
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
import os
import re
import sys
def check_file(file_path):
errors = []
warnings = []
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
lines = content.splitlines()
seen_urls = set()
for line_num, line in enumerate(lines, 1):
# 1. Check section titles (H2-H6) for Emojis, Special Characters, and Ampersands (Mandate 32)
if line.startswith("#"):
header_level = len(line) - len(line.lstrip('#'))
if 2 <= header_level <= 6:
title_text = line.lstrip('#').strip()
# Check for ampersand
if "&" in title_text:
errors.append(f"Line {line_num}: Title contains ampersand '&': '{title_text}' (replace with 'and')")
# Check for emojis or special characters
for char in title_text:
# Allow alphanumeric, spaces, hyphens, colons, parentheses, commas, periods, quotes
if ord(char) > 0x2000 and ord(char) not in (0x2013, 0x2014, 0x2018, 0x2019, 0x201c, 0x201d):
errors.append(f"Line {line_num}: Title contains emojis or special characters: '{char}' in '{title_text}'")
# 2. Check for Markdown link rules (Mandate 33)
# Match [ Link ](URL) - space at start or end of brackets
if re.search(r'\[\s+[^\]]*\]\(', line) or re.search(r'\[[^\]]*\s+\]\(', line):
errors.append(f"Line {line_num}: Link text contains leading or trailing spaces inside brackets: '{line.strip()}'")
# 3. Extract and validate URLs
links = re.findall(r'\[([^\]]+)\]\(([^)]+)\)', line)
for link_text, url in links:
url = url.strip()
if url.startswith("http://") or url.startswith("https://"):
if url.startswith("https:/") and not url.startswith("https://"):
errors.append(f"Line {line_num}: Corrupted protocol prefix: '{url}'")
elif url.startswith("http:/") and not url.startswith("http://"):
errors.append(f"Line {line_num}: Corrupted protocol prefix: '{url}'")
# Check for duplicates in this category file
if os.path.basename(file_path) not in ["index.md", "about.md"]:
# Normalize URL to check duplicate
clean_url = url.split("?")[0].split("#")[0].lower().rstrip('/')
if clean_url in seen_urls:
# Report as warning rather than blocking error
warnings.append(f"Line {line_num}: Duplicate URL found in this file: '{url}'")
else:
seen_urls.add(clean_url)
# Check year tag if present
match_year = re.search(r'\*\*\(([0-9]{4})\)\*\*', line)
if match_year:
year = int(match_year.group(1))
if not (1990 <= year <= 2030):
errors.append(f"Line {line_num}: Year tag '{year}' is outside reasonable bounds: '{line.strip()}'")
# Global multi-line check for link text line breaks
if re.search(r'\[[^\]]*\n[^\]]*\]\(', content):
errors.append("Global: Found a link with a line break inside the link text brackets.")
return errors, warnings
def main():
docs_dir = "docs"
if not os.path.exists(docs_dir):
print(f"Directory {docs_dir} not found.")
sys.exit(0)
total_errors = 0
total_warnings = 0
for root, dirs, files in os.walk(docs_dir):
if "images" in root or "static" in root:
continue
for file in files:
if file.endswith(".md"):
path = os.path.join(root, file)
errors, warnings = check_file(path)
if errors:
print(f"{path}:")
for err in errors:
print(f" {err}")
total_errors += len(errors)
if warnings:
# By default do not flood output with duplicate warnings unless verbose
if "--verbose" in sys.argv:
print(f"⚠️ {path}:")
for warn in warnings:
print(f" {warn}")
total_warnings += len(warnings)
print(f"\nScan complete: Found {total_errors} errors and {total_warnings} warnings in markdown files.")
if total_errors > 0:
sys.exit(1)
else:
print("All Markdown files passed the schema check successfully!")
sys.exit(0)
if __name__ == "__main__":
main()
+5 -2
View File
@@ -12,7 +12,9 @@ V2_DIR = "v2-docs"
def run_command(cmd):
try:
return subprocess.check_output(cmd, shell=True).decode('utf-8').strip()
except: return "0"
except Exception as e:
print(f"[WARN] Command '{cmd[:60]}' failed: {str(e)[:100]}")
return "0"
def clean_text(text: str) -> str:
"""Strips emojis and ampersands for README compatibility."""
@@ -26,7 +28,8 @@ def get_stats():
inventory = {}
try:
inventory = load_inventory()
except: pass
except Exception as e:
print(f"[WARN] Failed to load inventory: {str(e)[:100]}")
# 2. Basic Metrics
total_links = len([u for u in inventory.keys() if not u.startswith("INTRO:")])
+2 -4
View File
@@ -1,5 +1,6 @@
import yaml
import os
from src.inventory_manager import load_inventory
# Map category IDs to their friendly names and outline border colors (V2 only)
CATEGORIES = {
@@ -13,10 +14,7 @@ CATEGORIES = {
}
def load_inventory_channels():
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
inventory_path = os.path.join(repo_root, 'data', 'inventory.yaml')
with open(inventory_path, 'r', encoding='utf-8') as f:
inventory = yaml.safe_load(f) or {}
inventory = load_inventory()
channels = []
for url, entry in inventory.items():
+111
View File
@@ -0,0 +1,111 @@
"""RSS 2.0 feed generator for the Nubenetes Intelligence Digest.
Reads data/news_digest.json and writes v2-docs/feed.xml with the top
items from the 3-month digest window across all tech categories.
"""
from __future__ import annotations
import json
import os
from datetime import datetime
from email.utils import format_datetime
from xml.sax.saxutils import escape
from src.logger import log_event
DIGEST_PATH = "data/news_digest.json"
OUTPUT_PATH = "v2-docs/feed.xml"
FEED_TITLE = "Nubenetes Intelligence Digest"
FEED_LINK = "https://nubenetes.com/"
FEED_DESCRIPTION = "AI-curated top picks from the Cloud Native & Kubernetes ecosystem"
FEED_LANGUAGE = "en"
ITEMS_PER_FEED = 20
TECH_CATS = [
"Kubernetes & Orchestration", "AI & Agents", "Security & Compliance",
"CI/CD & GitOps", "Observability, SRE & Testing", "Infrastructure as Code",
"Containers & Runtime", "Networking & Service Mesh", "Cloud Providers & FinOps",
"MLOps & Data Science", "Data, Messaging & Storage",
]
def _rfc822(dt: datetime) -> str:
return format_datetime(dt)
def generate_rss() -> None:
if not os.path.exists(DIGEST_PATH):
log_event("[WARN] rss_generator: news_digest.json not found, skipping RSS generation")
return
try:
with open(DIGEST_PATH, "r", encoding="utf-8") as f:
digest = json.load(f)
except Exception as e:
log_event(f"[WARN] rss_generator: failed to load digest: {str(e)[:100]}")
return
period_data = digest.get("3_months", {})
items: list[dict] = []
for cat in TECH_CATS:
for entry in period_data.get(cat, []):
items.append({**entry, "_cat": cat})
# Sort by impact then date
impact_rank = {"critical": 3, "high": 2, "medium": 1}
items.sort(
key=lambda x: (impact_rank.get(x.get("impact", "medium"), 0), x.get("date", "")),
reverse=True,
)
items = items[:ITEMS_PER_FEED]
build_date = _rfc822(datetime.utcnow())
lines = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">',
" <channel>",
f" <title>{escape(FEED_TITLE)}</title>",
f" <link>{FEED_LINK}</link>",
f" <description>{escape(FEED_DESCRIPTION)}</description>",
f" <language>{FEED_LANGUAGE}</language>",
f" <lastBuildDate>{build_date}</lastBuildDate>",
f' <atom:link href="{FEED_LINK}feed.xml" rel="self" type="application/rss+xml"/>',
]
for item in items:
title = escape(item.get("title", "Unknown"))
url = item.get("url", "#")
why = escape(item.get("why", ""))
cat = escape(item.get("_cat", ""))
impact = item.get("impact", "medium")
date_str = item.get("date", "")
try:
pub_date = _rfc822(datetime.strptime(date_str, "%Y-%m-%d")) if date_str else build_date
except Exception:
pub_date = build_date
lines += [
" <item>",
f" <title>{title}</title>",
f" <link>{url}</link>",
f" <guid isPermaLink=\"true\">{url}</guid>",
f" <pubDate>{pub_date}</pubDate>",
f" <category>{cat}</category>",
f" <description>[{impact.upper()}] {why}</description>",
" </item>",
]
lines += [" </channel>", "</rss>"]
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
f.write("\n".join(lines) + "\n")
log_event(f"[INFO] rss_generator: wrote {len(items)} items to {OUTPUT_PATH}")
if __name__ == "__main__":
generate_rss()
+32 -22
View File
@@ -6,6 +6,12 @@ from datetime import datetime
from src.logger import log_event
from src.gemini_utils import normalize_url, clean_toc_text
from src.config import INVENTORY_PATH
from src.inventory_manager import load_inventory
try:
from yaml import CSafeLoader as Loader
except ImportError:
from yaml import SafeLoader as Loader
V1_DIR = "docs"
V2_DIR = "v2-docs"
@@ -20,19 +26,16 @@ class SafetyGuard:
self.inventory = self._load_inventory()
def _load_inventory(self):
if os.path.exists(INVENTORY_PATH):
try:
with open(INVENTORY_PATH, "r") as f:
return yaml.safe_load(f) or {}
except: return {}
return {}
return load_inventory()
def _load_exempt_files(self):
try:
with open("data/link_rules.yaml", "r") as f:
rules = yaml.safe_load(f)
with open("data/link_rules.yaml", "r", encoding="utf-8") as f:
rules = yaml.load(f, Loader=Loader)
return rules.get("hierarchy_rules", {}).get("toc_exempt_files", [])
except: return []
except Exception as e:
log_event(f"[WARN] load exempt files from link_rules.yaml: {str(e)[:100]}")
return []
def validate_data_integrity(self, old_inventory: dict):
"""Mandate 1: Información Preservation."""
@@ -59,9 +62,11 @@ class SafetyGuard:
"""Mandate 27: Special Assets Exhaustive Inclusion."""
if not os.path.exists(SPECIAL_ASSETS_PATH): return
try:
with open(SPECIAL_ASSETS_PATH, "r") as f:
special = yaml.safe_load(f).get("special_assets", [])
except: return
with open(SPECIAL_ASSETS_PATH, "r", encoding="utf-8") as f:
special = yaml.load(f, Loader=Loader).get("special_assets", [])
except Exception as e:
log_event(f"[WARN] load special_assets.yaml for validation: {str(e)[:100]}")
return
for sa in special:
if "Include 100%" in sa.get("v2_rule", "") or "Exhaustive" in sa.get("v2_rule", ""):
@@ -88,7 +93,8 @@ class SafetyGuard:
stars = meta.get("gh_stars", meta.get("stars", 0) * 100)
if inactive_years > 4 and stars < 30:
self.warnings.append(f"🏚️ **MVQ Violation**: Stale repo `{url}` (>4yrs) in V2 with low impact")
except: pass
except Exception as e:
log_event(f"[WARN] MVQ compliance check for {url}: {str(e)[:100]}")
def validate_linguistic_tagging(self):
"""Mandate 10: Explicit Language Tagging."""
@@ -175,9 +181,11 @@ class SafetyGuard:
"""Mandate 11: Workflow-Config Synchronization."""
if not os.path.exists(WORKFLOW_PATH) or not os.path.exists(CURATION_SOURCES_PATH): return
try:
with open(CURATION_SOURCES_PATH, "r") as f:
sources = yaml.safe_load(f).get("sources", [])
except: return
with open(CURATION_SOURCES_PATH, "r", encoding="utf-8") as f:
sources = yaml.load(f, Loader=Loader).get("sources", [])
except Exception as e:
log_event(f"[WARN] load curation_sources.yaml for nav sync: {str(e)[:100]}")
return
topics = [s["topic"] for s in sources]
with open(WORKFLOW_PATH, "r") as f:
@@ -191,10 +199,11 @@ class SafetyGuard:
def validate_forbidden_tags(self):
"""Mandate 51/Safety: Check for forbidden HTML tags in docs and v2-docs, except allowing iframes in videos."""
try:
with open("data/link_rules.yaml", "r") as f:
rules = yaml.safe_load(f)
with open("data/link_rules.yaml", "r", encoding="utf-8") as f:
rules = yaml.load(f, Loader=Loader)
forbidden = rules.get("safety_guard", {}).get("forbidden_tags", [])
except:
except Exception as e:
log_event(f"[WARN] load forbidden tags from link_rules.yaml: {str(e)[:100]}")
return
if not forbidden:
@@ -275,9 +284,10 @@ class SafetyGuard:
# 2. Run standard validations
if old_inv_path and os.path.exists(old_inv_path):
try:
with open(old_inv_path, "r") as f:
self.validate_data_integrity(yaml.safe_load(f) or {})
except: pass
with open(old_inv_path, "r", encoding="utf-8") as f:
self.validate_data_integrity(yaml.load(f, Loader=Loader) or {})
except Exception as e:
log_event(f"[WARN] load old inventory for data integrity check: {str(e)[:100]}")
self.validate_semantic_interlinking()
self.validate_special_assets_completeness()
+1 -1
View File
@@ -8,7 +8,7 @@ REQUIRED_SECTIONS = [
"3. The Agentic Stack",
"4. The 2026 Architectural Shift",
"5. Dual-Edition Architecture (V1 vs V2)",
"6. The Unified Agentic Database (Knowledge Graph)",
"6. The Unified Agentic Database (Coexistence Knowledge Graph)",
"7. AI Economic Architecture and Cost Analysis",
"8. The Agentic AI Engine",
"9. GitHub Workflows and Automation",
+7 -2
View File
@@ -6,6 +6,11 @@ from src.logger import log_event
CURATION_SOURCES_PATH = "data/curation_sources.yaml"
WORKFLOW_PATH = ".github/workflows/01.1.agentic_cron.yml"
try:
from yaml import CSafeLoader as Loader
except ImportError:
from yaml import SafeLoader as Loader
class WorkflowUISync:
"""
Automates Mandate 11: Workflow-Config Synchronization.
@@ -16,8 +21,8 @@ class WorkflowUISync:
return False
try:
with open(CURATION_SOURCES_PATH, "r") as f:
sources = yaml.safe_load(f).get("sources", [])
with open(CURATION_SOURCES_PATH, "r", encoding="utf-8") as f:
sources = yaml.load(f, Loader=Loader).get("sources", [])
except Exception as e:
log_event(f" [!] Error loading curation sources: {e}")
return False
+43 -10
View File
@@ -31,6 +31,27 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
tags = item.get("tags", [])
initial_score = item.get("impact_score", item.get("stars", 3) * 20) # Fallback mapping if stars is used
# 0. Check cache using content hash (Recommendation #4)
import hashlib
raw_content = f"{title}||{desc}||{','.join(sorted(tags))}"
content_hash = hashlib.sha256(raw_content.encode("utf-8")).hexdigest()
if os.path.exists(DEBATE_MEMORY_FILE):
try:
with open(DEBATE_MEMORY_FILE, "r") as f:
memory_data = json.load(f)
cached = memory_data.get("resolved_debates", {}).get(normalize_url(url))
if cached and cached.get("content_hash") == content_hash:
log_event(f" [⚖️] CACHE HIT: Skipping debate for '{title}'. Returning cached consensus.")
return (
cached["final_consensus_score"],
cached.get("final_tags", tags),
cached.get("refined_summary", desc),
cached
)
except Exception as e:
log_event(f" [!] Error checking debate cache: {e}")
log_event(f" [⚖️] DEBATE TRIGGERED: '{title}' (Initial Score: {initial_score})", section_break=False)
# 0. Check if mock mode is requested or required (no keys configured)
@@ -128,7 +149,10 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
"scores": scores,
"justifications": justifications,
"rebuttals": debate_transcript,
"timestamp": datetime.now().isoformat()
"timestamp": datetime.now().isoformat(),
"final_tags": sorted(list(final_tags)),
"refined_summary": refined_summary,
"content_hash": content_hash
}
try:
@@ -136,13 +160,14 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
if os.path.exists(DEBATE_MEMORY_FILE):
try:
memory_data = json.load(open(DEBATE_MEMORY_FILE, "r"))
except: pass
except Exception as e:
log_event(f"[WARN] load debate memory for mock persist: {str(e)[:100]}")
memory_data.setdefault("resolved_debates", {})[normalize_url(url)] = debate_data
with open(DEBATE_MEMORY_FILE, "w") as f:
json.dump(memory_data, f, indent=2)
except Exception as e:
log_event(f" [!] Failed to persist debate memory: {e}")
return final_score, sorted(list(final_tags)), refined_summary, debate_data
system_mandates = get_system_mandates()
@@ -187,7 +212,10 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
"final_consensus_score": fast_pass_score,
"fast_pass": True,
"justification": fast_pass_justification,
"timestamp": datetime.now().isoformat()
"timestamp": datetime.now().isoformat(),
"final_tags": fast_pass_tags,
"refined_summary": fast_pass_summary,
"content_hash": content_hash
}
try:
@@ -195,13 +223,14 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
if os.path.exists(DEBATE_MEMORY_FILE):
try:
memory_data = json.load(open(DEBATE_MEMORY_FILE, "r"))
except: pass
except Exception as e:
log_event(f"[WARN] load debate memory for fast-pass persist: {str(e)[:100]}")
memory_data.setdefault("resolved_debates", {})[normalize_url(url)] = debate_data
with open(DEBATE_MEMORY_FILE, "w") as f:
json.dump(memory_data, f, indent=2)
except Exception as e:
log_event(f" [!] Failed to persist debate memory: {e}")
return fast_pass_score, fast_pass_tags, fast_pass_summary, debate_data
log_event(f" [⚖️] Borderline score detected ({fast_pass_score}). Escalating to full Multi-Agent Debate Panel...")
@@ -325,7 +354,10 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
"scores": scores,
"justifications": justifications,
"rebuttals": debate_transcript,
"timestamp": datetime.now().isoformat()
"timestamp": datetime.now().isoformat(),
"final_tags": final_tags,
"refined_summary": refined_summary,
"content_hash": content_hash
}
# Persist the resolved debate to memory log (Mandate 3.1)
@@ -334,10 +366,11 @@ async def run_debate_protocol(item: Dict, is_new_link: bool = False) -> Tuple[in
if os.path.exists(DEBATE_MEMORY_FILE):
try:
memory_data = json.load(open(DEBATE_MEMORY_FILE, "r"))
except: pass
except Exception as e:
log_event(f"[WARN] load debate memory for final persist: {str(e)[:100]}")
memory_data.setdefault("resolved_debates", {})[normalize_url(url)] = debate_data
# Keep blacklist and other fields intact
with open(DEBATE_MEMORY_FILE, "w") as f:
json.dump(memory_data, f, indent=2)
+289 -67
View File
@@ -4,6 +4,10 @@ import json
import hashlib
import asyncio
import yaml
try:
from yaml import CSafeLoader as Loader
except ImportError:
from yaml import SafeLoader as Loader
import httpx
from datetime import datetime
from typing import List, Dict, Set, Any, Tuple
@@ -37,14 +41,30 @@ class V2VisionEngine:
"AI": ["ai", "ai-agents-mcp", "chatgpt", "mlops"],
"Architectural Foundations": ["introduction", "faq", "kubernetes", "linux", "git", "cloud-arch-diagrams", "matrix-table", "other-awesome-lists", "about"],
"Platform & Site Reliability": ["sre", "devops", "developerportals", "scaffolding", "finops", "chaos-engineering", "performance-testing-with-jenkins-and-jmeter", "project-management-methodology", "project-management-tools", "qa", "test-automation-frameworks", "testops"],
"Hardened Infrastructure": ["iac", "terraform", "pulumi", "crossplane", "ansible", "securityascode", "kubernetes-security", "aws-security", "oauth", "devsecops", "kustomize", "liquibase", "chef"],
"Cloud Providers (Hyperscalers)": ["aws", "azure", "GoogleCloudPlatform", "ibm_cloud", "oraclecloud", "digitalocean", "cloudflare", "scaleway", "managed-kubernetes-in-public-cloud", "public-cloud-solutions", "private-cloud-solutions", "edge-computing", "aws-architecture", "aws-security", "aws-networking", "aws-databases", "aws-storage", "aws-monitoring", "aws-iac", "aws-tools-scripts", "aws-messaging", "aws-data", "aws-devops", "aws-serverless", "aws-containers", "aws-backup", "aws-training", "aws-newfeatures", "aws-miscellaneous", "aws-pricing", "aws-spain"],
"Hardened Infrastructure": ["iac", "terraform", "pulumi", "crossplane", "ansible", "securityascode", "kubernetes-security", "aws-security", "devsecops", "kustomize", "liquibase"],
"Cloud Providers (Hyperscalers)": ["aws", "azure", "GoogleCloudPlatform", "ibm_cloud", "oraclecloud", "digitalocean", "cloudflare", "managed-kubernetes-in-public-cloud", "public-cloud-solutions", "edge-computing", "aws-architecture", "aws-security", "aws-networking", "aws-databases", "aws-storage", "aws-monitoring", "aws-iac", "aws-tools-scripts", "aws-messaging", "aws-data", "aws-devops", "aws-serverless", "aws-containers", "aws-backup", "aws-training", "aws-newfeatures", "aws-miscellaneous", "aws-pricing"],
"Networking & Service Mesh": ["networking", "kubernetes-networking", "servicemesh", "istio", "caching", "web-servers", "cloudflare"],
"The Container Stack": ["docker", "container-managers", "serverless", "kubernetes-autoscaling", "kubernetes-operators-controllers", "kubernetes-storage", "kubernetes-monitoring", "kubernetes-troubleshooting", "kubernetes-backup-migrations", "kubernetes-on-premise", "kubernetes-bigdata", "kubernetes-client-libraries", "kubernetes-releases", "kubernetes-based-devel", "kubernetes-alternatives", "kubectl-commands", "rancher", "openshift", "ocp3", "ocp4", "noops"],
"Data & Advanced Analytics": ["databases", "nosql", "newsql", "message-queue", "crunchydata", "yaml", "bigdata"],
"Engineering Pipeline": ["cicd", "gitops", "argo", "flux", "tekton", "jenkins", "jenkins-alternatives", "openshift-pipelines", "sonarqube", "registries", "keptn", "stackstorm", "cicd-kubernetes-plugins"],
"Developer Ecosystem": ["visual-studio", "javascript", "golang", "python", "java_frameworks", "java_app_servers", "java-and-java-performance-optimization", "dotnet", "angular", "react", "web3", "api", "swagger-code-generator-for-rest-apis", "postman", "lowcode-nocode", "devel-sites", "dom", "linux-dev-env", "ChromeDevTools", "xamarin", "jvm-parameters-matrix-table", "maven-gradle", "embedded-servlet-containers"],
"Career & Industry": ["recruitment", "hr", "finops", "freelancing", "remote-tech-jobs", "workfromhome", "interview-questions", "elearning", "digital-money", "appointment-scheduling", "newsfeeds"]
"Data & Advanced Analytics": ["databases", "nosql", "message-queue", "crunchydata", "yaml", "bigdata"],
"Engineering Pipeline": ["cicd", "gitops", "argo", "flux", "tekton", "jenkins", "jenkins-alternatives", "openshift-pipelines", "sonarqube", "registries", "keptn", "cicd-kubernetes-plugins"],
"Developer Ecosystem": ["visual-studio", "javascript", "golang", "python", "java_frameworks", "java_app_servers", "java-and-java-performance-optimization", "dotnet", "angular", "web3", "api", "swagger-code-generator-for-rest-apis", "postman", "lowcode-nocode", "devel-sites", "linux-dev-env", "ChromeDevTools", "maven-gradle", "embedded-servlet-containers"],
"Career & Industry": ["recruitment", "hr", "finops", "freelancing", "remote-tech-jobs", "workfromhome", "interview-questions", "elearning", "appointment-scheduling", "newsfeeds"]
}
# Stub page merge map: content from source pages renders on target pages
self.merge_map = {
"jvm-parameters-matrix-table": "java-and-java-performance-optimization",
"private-cloud-solutions": "kubernetes-on-premise",
"stackstorm": "cicd",
"chef": "ansible",
"newsql": "databases",
"scaleway": "digitalocean",
"xamarin": "dotnet",
"dom": "javascript",
"react": "javascript",
"oauth": "securityascode",
"digital-money": "finops",
"aws-spain": "aws",
}
self.library_criteria = (
@@ -74,15 +94,23 @@ class V2VisionEngine:
def _load_special_assets(self) -> Dict:
path = "data/special_assets.yaml"
if os.path.exists(path):
try: return yaml.safe_load(open(path, "r")) or {}
except: return {}
try:
with open(path, "r", encoding="utf-8") as f:
return yaml.load(f, Loader=Loader) or {}
except Exception as e:
log_event(f"[WARN] load special_assets.yaml: {str(e)[:100]}")
return {}
return {}
def _load_link_rules(self) -> Dict:
path = "data/link_rules.yaml"
if os.path.exists(path):
try: return yaml.safe_load(open(path, "r")) or {}
except: return {}
try:
with open(path, "r", encoding="utf-8") as f:
return yaml.load(f, Loader=Loader) or {}
except Exception as e:
log_event(f"[WARN] load link_rules.yaml: {str(e)[:100]}")
return {}
return {}
def _load_inventory(self) -> Dict:
@@ -98,18 +126,20 @@ class V2VisionEngine:
# Mandate 30: MD039 - Global Data Sanitization (Purge all whitespace/hidden chars from titles)
for url in list(self.inventory.keys()):
if isinstance(self.inventory[url], dict) and "title" in self.inventory[url]:
# Purge all known whitespace characters (standard, non-breaking, thin, etc.)
if isinstance(self.inventory[url], dict) and self.inventory[url].get("title") is not None:
t = self.inventory[url]["title"]
t = re.sub(r'^[\s\u00a0\u200b\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+', '', t)
t = re.sub(r'[\s\u00a0\u200b\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+$', '', t)
self.inventory[url]["title"] = t
if isinstance(t, str):
# Purge all known whitespace characters (standard, non-breaking, thin, etc.)
t = re.sub(r'^[\s\u00a0\u200b\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+', '', t)
t = re.sub(r'[\s\u00a0\u200b\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+$', '', t)
self.inventory[url]["title"] = t
# 0. Mandate Sync
try:
from src.mandate_ingestor import MandateIngestor
MandateIngestor().save_system_instructions()
except: pass
except Exception as e:
log_event(f"[WARN] mandate sync: {str(e)[:100]}")
all_v1_links, mosaic_html, videos_html = await self._gather_all_v1_content()
@@ -139,21 +169,35 @@ class V2VisionEngine:
# --- SURGICAL GARBAGE COLLECTION ---
# Track every file we generate
generated_files = {"index.md", "audit-log.md", "videos.md", "tags.md"}
generated_files = {"index.md", "audit-log.md", "videos.md", "tags.md", "tech-digest.md", "industry-digest.md"}
for f_name in v2_data.keys():
generated_files.add(f_name)
await self._write_premium_files(v2_data, mosaic_html, videos_html)
self._generate_digest_pages()
await self._generate_global_tag_index(v2_data)
await self._sync_enterprise_navigation(v2_data)
# Delete only orphaned files
log_event("[*] Phase 5: Pruning Orphaned V2 Assets...")
for f in os.listdir(V2_DIR):
if f.endswith(".md") and f not in generated_files:
log_event(f" [DEL] Pruning obsolete V2 page: {f}")
os.remove(os.path.join(V2_DIR, f))
# Phase 5: Structural changes — ONLY in full (non-render-only) mode
# In render-only mode we never delete pages or rewrite the nav, because
# the inventory pass is conservative and some pages may not be regenerated
# in a given run even though they should still exist (e.g. low-hit pages).
# Deleting them would break MkDocs nav references and corrupt the site.
if self.render_only:
log_event("[*] Phase 5: Skipped (render-only mode — nav and pages preserved)")
else:
log_event("[*] Phase 5: Syncing navigation and pruning orphaned pages...")
nav_ok = await self._sync_enterprise_navigation(v2_data)
if nav_ok:
# Only prune if nav was successfully updated, and never delete
# pages that are defined in self.dimensions (expected to exist).
dimension_pages = {f"{slug}.md" for pages in self.dimensions.values() for slug in pages}
for f in os.listdir(V2_DIR):
if f.endswith(".md") and f not in generated_files and f not in dimension_pages:
log_event(f" [DEL] Pruning truly orphaned V2 page: {f}")
os.remove(os.path.join(V2_DIR, f))
else:
log_event("[WARN] Phase 5: Nav sync failed — skipping page deletion to avoid corruption")
self._save_inventory()
@@ -197,7 +241,11 @@ class V2VisionEngine:
if not url.startswith(("http", "mailto", "#")):
url = f"https://nubenetes.com/{url.replace('.md', '/')}"
# Mandate 30: MD039 - Strip all whitespace (including non-breaking space) from link text
all_links.append({"title": nuclear_strip(title), "url": url.strip(), "description": full_desc.strip(), "original_file": file})
orig_file = file
slug = file.replace(".md", "")
if slug in self.merge_map:
orig_file = self.merge_map[slug] + ".md"
all_links.append({"title": nuclear_strip(title), "url": url.strip(), "description": full_desc.strip(), "original_file": orig_file})
return all_links, mosaic_html, videos_html
async def _verify_link_health(self, links: List[Dict]):
@@ -212,7 +260,11 @@ class V2VisionEngine:
if entry.get("status") == "review_required": continue
if not force_full and entry.get("status") == "online":
fast_online.append(l)
last_checked = entry.get("last_checked", 0)
if isinstance(last_checked, (int, float)) and (datetime.now().timestamp() - last_checked) > 30 * 86400:
needs_check.append(l)
else:
fast_online.append(l)
else:
needs_check.append(l)
@@ -261,7 +313,8 @@ class V2VisionEngine:
# Mandate 22: Update last_checked for the inventory entry
self.inventory[normalize_url(final_url)]["last_checked"] = datetime.now().timestamp()
return link
except: pass
except Exception as e:
log_event(f"[WARN] resilient link check for {url}: {str(e)[:100]}")
return None
async def _evaluate_and_score_resources(self, links: List[Dict]):
@@ -333,8 +386,16 @@ class V2VisionEngine:
if is_special: item["is_special"] = True
# Mandate 30: Hierarchy and AI Summaries are mandatory for ELITE AI curation.
# Optimized Skip Logic: Only skip if we already have BOTH hierarchy and a summary.
if ((cached.get("hierarchy") and cached.get("ai_summary")) or self.render_only) and not force_eval:
if project_id not in project_registry or item.get("stars", 0) > project_registry[project_id].get("stars", 0):
last_eval = cached.get("last_ai_eval", "")
eval_stale = False
if last_eval and isinstance(last_eval, str) and len(last_eval) >= 10:
try:
eval_age = (datetime.now(MADRID_TZ) - datetime.fromisoformat(last_eval)).days
eval_stale = eval_age > 180
except Exception:
pass
if ((cached.get("hierarchy") and cached.get("ai_summary") and not eval_stale) or self.render_only) and not force_eval:
if project_id not in project_registry or item.get("stars") or 0 > project_registry[project_id].get("stars") or 0:
if project_id in project_registry and project_registry[project_id].get("is_special"): item["is_special"] = True
project_registry[project_id] = item
continue
@@ -396,17 +457,21 @@ class V2VisionEngine:
if idx < len(batch_links):
item = batch_links[idx].copy()
eval_data = {
"year": str(res.get("year", "N/A")), "stars": min(max(int(res.get("stars", 0)), 0), 5),
"year": str(res.get("year", "N/A")), "stars": min(max(int(res.get("stars") or 0), 0), 5),
"ai_summary": res.get("summary", item.get("ai_summary", "")),
"language": res.get("language", "English"),
"resource_type": res.get("type", "Reference"), "complexity": res.get("complexity", "Intermediate"),
"hierarchy": res.get("hierarchy", ["General"]), "tags": res.get("tags", []),
"is_microservice": bool(res.get("is_microservice", False)),
"status": "online", "is_special": item.get("is_special", False)
"status": "online", "is_special": item.get("is_special", False),
"last_ai_eval": datetime.now(MADRID_TZ).isoformat()
}
existing_entry = self.inventory.get(normalize_url(item["url"]), {})
if existing_entry.get("discovered_at"):
eval_data["discovered_at"] = existing_entry["discovered_at"]
item.update(eval_data)
batch_results.append(item)
# Incremental Persistence
norm_url = normalize_url(item["url"])
from src.inventory_manager import update_inventory_entry
@@ -483,13 +548,17 @@ class V2VisionEngine:
if idx < len(batch):
item = batch[idx].copy()
eval_data = {
"year": str(res.get("year", "N/A")), "stars": min(max(int(res.get("stars", 0)), 0), 5),
"year": str(res.get("year", "N/A")), "stars": min(max(int(res.get("stars") or 0), 0), 5),
"ai_summary": res.get("summary", ""), "language": res.get("language", "English"),
"resource_type": res.get("type", "Reference"), "complexity": res.get("complexity", "Intermediate"),
"hierarchy": res.get("hierarchy", ["General"]), "tags": res.get("tags", []),
"is_microservice": bool(res.get("is_microservice", False)),
"status": "online", "is_special": item.get("is_special", False)
"status": "online", "is_special": item.get("is_special", False),
"last_ai_eval": datetime.now(MADRID_TZ).isoformat()
}
existing_entry = self.inventory.get(normalize_url(item["url"]), {})
if existing_entry.get("discovered_at"):
eval_data["discovered_at"] = existing_entry["discovered_at"]
item.update(eval_data)
analyst_results.append(item)
except Exception:
@@ -503,7 +572,7 @@ class V2VisionEngine:
l for l in analyst_results
if "[DE FACTO STANDARD]" in l.get("tags", [])
or "[ENTERPRISE-STABLE]" in l.get("tags", [])
or l.get("stars", 0) in [3, 4]
or l.get("stars") or 0 in [3, 4]
]
if debate_candidates:
@@ -540,7 +609,7 @@ class V2VisionEngine:
new_data["addition_method"] = "manual"
update_inventory_entry(self.inventory, norm_url, new_data)
if p_id not in project_registry or item.get("stars", 0) > project_registry[p_id].get("stars", 0):
if p_id not in project_registry or item.get("stars") or 0 > project_registry[p_id].get("stars") or 0:
if p_id in project_registry and project_registry[p_id].get("is_special"): item["is_special"] = True
project_registry[p_id] = item
@@ -573,7 +642,7 @@ class V2VisionEngine:
# 1. GitHub Objective Reality (Mandate 43)
raw_gh = item.get("gh_stars", 0)
gh_stars = int(raw_gh) if str(raw_gh).isdigit() else 0
curator_stars = int(item.get("stars", 0))
curator_stars = int(item.get("stars") or 0)
if gh_stars > 15000 or curator_stars >= 5:
tags.add("[DE FACTO STANDARD]")
@@ -583,7 +652,7 @@ class V2VisionEngine:
if "[COMMUNITY-TOOL]" in tags: tags.remove("[COMMUNITY-TOOL]")
# 2. Type Mapping (AI based labels)
res_type = item.get("resource_type", "Reference").lower()
res_type = (item.get("resource_type") or "Reference").lower()
if any(x in res_type for x in ["guide", "tutorial", "hands-on", "learning", "course"]):
tags.add("[GUIDE]")
if any(x in res_type for x in ["case study", "report", "whitepaper", "success story", "usage"]):
@@ -643,7 +712,7 @@ class V2VisionEngine:
# Mandate 29: Special Assets must include 100% of ALIVE links, bypassing impact filters.
is_special = item.get("is_special", False) or orig_file in special_rules
if not is_special and orig_file == "introduction.md" and item.get("stars", 0) < 3 and not item.get("is_microservice"):
if not is_special and orig_file == "introduction.md" and item.get("stars") or 0 < 3 and not item.get("is_microservice"):
continue
if orig_file not in v2_structure:
@@ -679,7 +748,7 @@ class V2VisionEngine:
audit_entry = {
"url": item["url"],
"tag": ", ".join(item["tags"]),
"stars": item.get("stars", 0),
"stars": item.get("stars") or 0,
"dimension": dim,
"v2_locations": True
}
@@ -697,7 +766,7 @@ class V2VisionEngine:
current["__links__"].append(item)
def sort_rec(node):
if "__links__" in node: node["__links__"].sort(key=lambda x: (-x.get("stars", 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
if "__links__" in node: node["__links__"].sort(key=lambda x: (-(x.get("stars") or 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
for k, v in node.items():
if k != "__links__" and isinstance(v, dict): sort_rec(v)
@@ -706,14 +775,27 @@ class V2VisionEngine:
return v2_structure
def _collect_tags_from_tree(self, node: Dict) -> List[Set]:
"""Recursively collect maturity/tech tags from a content tree for cross-referencing."""
results = []
if "__links__" in node:
for link in node["__links__"]:
tags = set(link.get("tags", []))
if tags:
results.append(tags)
for key, val in node.items():
if key != "__links__" and isinstance(val, dict):
results.extend(self._collect_tags_from_tree(val))
return results
async def _generate_comparison_table(self, links: List[Dict]) -> str:
standard_tools = [l for l in links if l.get("stars", 0) >= 3]
if len(standard_tools) < 5: return ""
standard_tools = [l for l in links if l.get("stars") or 0 >= 3]
if len(standard_tools) < 8: return ""
table = "\n??? abstract \"Architect's Technical Comparison Table\"\n"
table += " | Solution | Maturity | Primary Focus | Language | Stars |\n"
table += " | :--- | :--- | :--- | :--- | :--- |\n"
for l in standard_tools[:10]:
stars = "🌟" * l.get("stars", 0)
stars = "🌟" * l.get("stars") or 0
focus = l.get("topic", l.get("hierarchy", ["General"])[-1])
# Mandate 30: MD039 - Strip all whitespace (including non-breaking space) from link text
clean_title = nuclear_strip(l['title'])
@@ -757,7 +839,7 @@ class V2VisionEngine:
async def _render_single_link(self, l: Dict, is_intro: bool) -> str:
md = ""
is_gold = is_intro and l.get("stars", 0) >= 4
is_gold = is_intro and l.get("stars") or 0 >= 4
title = nuclear_strip(l['title'])
if is_gold:
img = f" ![Preview]({l.get('social_preview_url')})\n" if l.get('social_preview_url') else ""
@@ -798,7 +880,7 @@ class V2VisionEngine:
tag_html += f" <span class='md-tag md-tag--{color}'>{tag}</span>"
# Apply Visual Highlighting based on stars
raw_stars = l.get('stars', 0)
raw_stars = l.get('stars') or 0
link_content = title
if raw_stars >= 5:
link_content = f"=={link_content}=="
@@ -826,7 +908,7 @@ class V2VisionEngine:
year = l.get("year", "")
year_prefix = f"**({year})** " if year and str(year).lower() != "n/a" else ""
raw_stars = l.get("stars", 0)
raw_stars = l.get("stars") or 0
stars_str = f" {'🌟' * raw_stars}" if raw_stars > 0 else ""
# Title formatting based on impact
@@ -863,10 +945,109 @@ class V2VisionEngine:
def _generate_digest_pages(self):
"""Generate tech-digest.md and industry-digest.md from news_digest.json."""
digest_path = "data/news_digest.json"
if not os.path.exists(digest_path):
log_event("[Digest] No digest data found, skipping page generation")
return
with open(digest_path, "r", encoding="utf-8") as f:
digest_data = json.load(f)
tech_cats = [
"Kubernetes & Orchestration", "Containers & Runtime", "Networking & Service Mesh",
"Architecture & Microservices", "Data, Messaging & Storage", "AI & Agents",
"MLOps & Data Science", "Python, Java & Developer Ecosystem", "Linux & System Foundations",
"Security & Compliance", "Infrastructure as Code", "CI/CD & GitOps",
"Observability, SRE & Testing", "DevOps & Culture", "Platform Engineering & DevEx",
"FinOps & Cloud Cost", "Certification & Training",
"AWS", "Azure", "GCP, OCI & Others", "OpenShift / Red Hat", "Virtualization & Private Cloud"
]
geo_cats = ["Americas", "Europe", "España", "Asia-Pacific"]
period_labels = {"3_months": "Last 3 Months", "6_months": "Last 6 Months", "12_months": "Last 12 Months"}
def render_digest_page(title, categories, digest_data, search_boost=1):
md = f"---\nsearch:\n boost: {search_boost}\n---\n\n"
md += f"# {title}\n\n"
md += "!!! tip \"Nubenetes Intelligence Digest\"\n"
md += " AI-curated ranking of the most impactful resources, updated monthly.\n\n"
for period_key, period_label in period_labels.items():
md += f'=== "{period_label}"\n\n'
period_data = digest_data.get(period_key, {})
for cat in categories:
items = period_data.get(cat, [])
if not items:
continue
md += f" **{cat}**\n\n"
md += " | Date | Resource | Impact | Why It Matters |\n"
md += " | :--- | :--- | :---: | :--- |\n"
for item in items:
impact_badge = {"critical": "🔴", "high": "🟡", "medium": "🔵"}.get(item.get("impact", "medium"), "🔵")
t = nuclear_strip(item.get("title", "Unknown"))
why = (item.get("why", "") or "").replace("|", "-").replace("\n", " ")
md += f' | {item.get("date", "")} | [{t}]({item.get("url", "#")}) | {impact_badge} {item.get("impact", "medium")} | {why} |\n'
md += "\n"
md += "\n"
return md
tech_md = render_digest_page("📊 Nubenetes Tech & Cloud Intelligence Digest", tech_cats, digest_data, search_boost=2)
with open(os.path.join(V2_DIR, "tech-digest.md"), "w", encoding="utf-8") as f:
f.write(tech_md)
industry_md = render_digest_page("🌍 Nubenetes Industry & Geo Intelligence Digest", geo_cats, digest_data, search_boost=2)
with open(os.path.join(V2_DIR, "industry-digest.md"), "w", encoding="utf-8") as f:
f.write(industry_md)
log_event("[Digest] Generated tech-digest.md and industry-digest.md")
async def _write_premium_files(self, data: Dict[str, Dict], mosaic_html: str, videos_html: str):
# 1. Update Index with Pulse
trending_pool = sorted([dict(meta, url=url) for url, meta in self.inventory.items() if isinstance(meta, dict) and meta.get("stars", 0) >= 4], key=lambda x: (str(x.get("year", "0000")) if str(x.get("year", "")).isdigit() else "0000", -x.get("stars", 0)), reverse=True)
pulse_md = "## The Agentic Pulse\n" + "\n".join([f"- **({l.get('year', 'N/A')})** [**=={nuclear_strip(l['title'])}==**]({l['url'].strip()}) {'🌟'*l.get('stars',3)}" for l in trending_pool[:5]])
# 1. Build Trending Now from digest data, or fallback to star-based pulse
digest_data = {}
digest_path = "data/news_digest.json"
if os.path.exists(digest_path):
try:
with open(digest_path, "r", encoding="utf-8") as df:
digest_data = json.load(df)
except Exception:
pass
if digest_data and "3_months" in digest_data:
top_items = []
for cat_name, items in digest_data.get("3_months", {}).items():
for item in items[:2]:
top_items.append({**item, "digest_category": cat_name})
top_items.sort(key=lambda x: {"critical": 3, "high": 2, "medium": 1}.get(x.get("impact", "medium"), 0), reverse=True)
top_items = top_items[:6]
impact_icons = {"critical": "🔴", "high": "🟡", "medium": "🔵"}
try:
digest_mtime = os.path.getmtime(digest_path)
from datetime import datetime as _dt
digest_updated = _dt.fromtimestamp(digest_mtime).strftime("%b %d, %Y")
except Exception:
digest_updated = ""
updated_badge = f'<span class="trending-section__updated">Updated {digest_updated}</span>' if digest_updated else ""
cards_html = f'<div class="trending-section">\n<div class="trending-section__title">🔥 Trending Now — Cloud Native Intelligence {updated_badge}</div>\n<div class="trending-grid">\n'
for item in top_items:
impact = item.get("impact", "medium")
cards_html += (
f'<div class="trending-card">\n'
f' <div class="trending-card__impact trending-card__impact--{impact}">{impact_icons.get(impact, "🔵")} {impact.upper()}</div>\n'
f' <div class="trending-card__category">{item.get("digest_category", "")}</div>\n'
f' <div class="trending-card__title"><a href="{item.get("url", "#")}">{nuclear_strip(item.get("title", "Unknown"))}</a></div>\n'
f' <div class="trending-card__meta">{item.get("date", "")} · {"🌟" * item.get("stars") or 0}</div>\n'
f' <div class="trending-card__why">{item.get("why", "")}</div>\n'
f'</div>\n'
)
cards_html += '</div>\n'
cards_html += '<div class="digest-links">\n'
cards_html += ' <a href="./tech-digest/" class="digest-link-card">📊 Full Tech & Cloud Digest →</a>\n'
cards_html += ' <a href="./industry-digest/" class="digest-link-card">🌍 Industry & Geo Digest →</a>\n'
cards_html += '</div>\n</div>\n'
pulse_md = cards_html
else:
trending_pool = sorted([dict(meta, url=url) for url, meta in self.inventory.items() if isinstance(meta, dict) and (meta.get("stars") or 0) >= 4], key=lambda x: (str(x.get("year", "0000")) if str(x.get("year", "")).isdigit() else "0000", -(x.get("stars") or 0)), reverse=True)
pulse_md = "## The Agentic Pulse\n" + "\n".join([f"- **({l.get('year', 'N/A')})** [**=={nuclear_strip(l['title'])}==**]({l['url'].strip()}) {'🌟'*l.get('stars',3)}" for l in trending_pool[:5]])
# Calculate coverage for the index
total_v1 = len(self.inventory)
@@ -902,7 +1083,7 @@ class V2VisionEngine:
"<center markdown=\"1\">\n"
"<div class=\"hero-showcase-wrapper\">\n"
" <a href=\"https://www.cncf.io/certification/software-conformance\" class=\"hero-showcase-link\">\n"
" <img src=\"images/container_with_cars_v2.png\" alt=\"container_with_cars\" class=\"hero-showcase-image\" />\n"
" <img src=\"/images/container_with_cars_v2.png\" alt=\"container_with_cars\" class=\"hero-showcase-image\" />\n"
" <div class=\"hero-showcase-footer\">\n"
" <span class=\"hero-showcase-badge\">CNCF Conformance</span>\n"
" <span class=\"hero-showcase-caption\">Standardized conformance guarantees seamless workload portability across the Cloud Native landscape.</span>\n"
@@ -933,6 +1114,13 @@ class V2VisionEngine:
" <div class=\"hero-badge-subtitle\">Agentic Ecosystem</div>\n"
" </div>\n"
" </a>\n"
" <a href=\"./tech-digest/\" style=\"text-decoration: none; color: inherit; display: block;\">\n"
" <div class=\"hero-badge-card hero-badge-card--amber\">\n"
" <div class=\"hero-badge-icon\">📊</div>\n"
" <div class=\"hero-badge-title\">Intelligence Digest</div>\n"
" <div class=\"hero-badge-subtitle\">Top picks · 3/6/12 months</div>\n"
" </div>\n"
" </a>\n"
" <a href=\"./videos/\" style=\"text-decoration: none; color: inherit; display: block;\">\n"
" <div class=\"hero-badge-card hero-badge-card--pink\">\n"
" <img src=\"/images/video_hub_logo.png\" alt=\"Agentic Video Hub\"/>\n"
@@ -1158,10 +1346,30 @@ class V2VisionEngine:
md += await render_node(info["content"], -1, f_name.replace(".md", ""), used_headers, is_intro=(f_name=="introduction.md" or f_name=="about.md"))
# Add Semantic "See Also" ONLY ONCE at the end of the page
related = [f"[{data[f]['title']}](./{f})" for f in data if f != f_name and data[f]["dim"] == info["dim"]]
if related:
md += f"\n---\n💡 **Explore Related:** {' | '.join(related[:3])}\n\n"
# Add Semantic "See Also" — same dimension + cross-dimension by shared tags
same_dim = [f for f in data if f != f_name and data[f]["dim"] == info["dim"]]
cross_dim = []
if info.get("content") and isinstance(info["content"], dict):
page_tags = set()
for node_links in self._collect_tags_from_tree(info["content"]):
page_tags.update(node_links)
if page_tags:
for f in data:
if f != f_name and data[f]["dim"] != info["dim"]:
other_tags = set()
if isinstance(data[f].get("content"), dict):
for t in self._collect_tags_from_tree(data[f]["content"]):
other_tags.update(t)
if page_tags & other_tags:
cross_dim.append(f)
related = [f"[{data[f]['title']}](./{f})" for f in same_dim[:3]]
cross = [f"[{data[f]['title']}](./{f})" for f in cross_dim[:2]]
if related or cross:
md += "\n---\n"
if related:
md += f"💡 **Explore Related:** {' | '.join(related)}\n\n"
if cross:
md += f"🔗 **See Also:** {' | '.join(cross)}\n\n"
# Smart Write: Only update disk if content changed
target_path = os.path.join(V2_DIR, f_name)
@@ -1258,7 +1466,7 @@ class V2VisionEngine:
md += f"<summary>{summary_text}</summary>\n\n"
# Sort links under this tag by impact stars and then by year
sorted_links = sorted(by_tag[tag], key=lambda x: (-x.get("stars", 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
sorted_links = sorted(by_tag[tag], key=lambda x: (-(x.get("stars") or 1), -(int(x["year"]) if str(x.get("year", "")).isdigit() else 0)))
rendered_links = sorted_links[:100]
for l in rendered_links:
@@ -1280,14 +1488,17 @@ class V2VisionEngine:
if md != existing_content:
with open(target_path, "w") as f: f.write(md)
async def _sync_enterprise_navigation(self, data: Dict[str, Dict]):
async def _sync_enterprise_navigation(self, data: Dict[str, Dict]) -> bool:
try:
with open("v2-mkdocs.yml", "r") as f: content = f.read()
nav = [
"nav:",
" - \"🔙 Back to V1 (Exhaustive)\": https://nubenetes.com/v1/",
"nav:",
" - \"🔙 Back to V1 (Exhaustive)\": https://nubenetes.com/v1/",
" - \"The 2026 Vision\": index.md",
" - \"Technical Tags\": tags.md",
" - \"Intelligence Digest\":",
" - \"Tech & Cloud Digest\": tech-digest.md",
" - \"Industry & Geo Digest\": industry-digest.md",
" - \"Agentic Video Hub\":",
" - videos/index.md",
" - \"AI Agents and MCP\": videos/ai-agents.md",
@@ -1295,8 +1506,7 @@ class V2VisionEngine:
" - \"Cloud Native Core\": videos/cloud-native.md",
" - \"Fundamentals\": videos/fundamentals.md"
]
# Group files by dimension
dim_groups = {}
for f_name, info in data.items():
dim_groups.setdefault(info["dim"], []).append(f_name)
@@ -1306,10 +1516,21 @@ class V2VisionEngine:
dim_nav = [f" - \"{dim}\":"]
for f in sorted(dim_groups[dim]):
dim_nav.append(f" - \"{data[f]['title']}\": {f}")
nav.extend(dim_nav)
updated = re.sub(r'nav:.*', "\n".join(nav), content, flags=re.DOTALL)
nav.extend(dim_nav)
# Replace only the nav section (from 'nav:' to end of file)
# Use a marker to ensure we don't accidentally eat extra_css etc.
if "nav:" not in content:
log_event("[WARN] _sync_enterprise_navigation: 'nav:' not found in v2-mkdocs.yml")
return False
nav_start = content.index("nav:")
updated = content[:nav_start] + "\n".join(nav) + "\n"
with open("v2-mkdocs.yml", "w") as f: f.write(updated)
except: pass
log_event(f" [OK] Nav synced: {len(nav)} entries written")
return True
except Exception as e:
log_event(f"[WARN] sync enterprise navigation: {str(e)[:100]}")
return False
import argparse
if __name__ == "__main__":
@@ -1361,7 +1582,8 @@ if __name__ == "__main__":
with open(os.path.join(V2_DIR, f), "r") as doc:
line = doc.readline()
if line.startswith("# "): title = line.replace("# ", "").strip()
except: pass
except Exception as e:
log_event(f"[WARN] extract title from V2 file {f}: {str(e)[:100]}")
file_list_md += f"| {i} | `{f}` | {title} |\n"
# 3. Decision Matrix (Maturity Audit)
@@ -1369,7 +1591,7 @@ if __name__ == "__main__":
header_table = "| # | Status | Maturity | Stars | Dimension | Resource |\n| :--- | :--- | :--- | :---: | :--- | :--- |\n"
for idx, entry in enumerate(engine.maturity_audit, 1):
status = "💎 ELITE" if entry.get('v2_locations') else "📦 ARCHIVE"
row = f"| {idx} | {status} | {entry.get('tag', 'N/A')} | {'🌟'*entry.get('stars',0)} | {entry.get('dimension', 'N/A')} | {entry.get('url', 'N/A')} |\n"
row = f"| {idx} | {status} | {entry.get('tag', 'N/A')} | {'🌟'*(entry.get('stars') or 0)} | {entry.get('dimension', 'N/A')} | {entry.get('url', 'N/A')} |\n"
matrix_rows.append(row)
# 4. Generate PR Body (Main Report)
+28 -9
View File
@@ -78,22 +78,41 @@ def extract_youtube_id(url):
def get_target_file(category, technology):
cat_lower = category.lower()
tech_lower = technology.lower()
if "agent" in tech_lower or "mcp" in tech_lower or "ai and future operations" in cat_lower:
if "agent" in tech_lower or "mcp" in tech_lower or "ai and future operations" in cat_lower or "llm" in tech_lower or "chatgpt" in tech_lower:
return "ai-agents.md", "AI Agents and MCP"
elif "infrastructure as code" in cat_lower or "security" in cat_lower or "observability" in cat_lower or "monitoring" in cat_lower or "devops" in tech_lower or "iac" in tech_lower or "sre" in tech_lower:
elif "mlops" in tech_lower or "data science" in cat_lower or "machine learning" in tech_lower:
return "ai-agents.md", "AI Agents and MCP"
elif "security" in cat_lower or "devsecops" in tech_lower or "zero trust" in tech_lower or "vulnerability" in tech_lower:
return "devops-iac.md", "DevOps, IaC, and SRE"
elif "fundamentals" in cat_lower:
elif "infrastructure as code" in cat_lower or "observability" in cat_lower or "monitoring" in cat_lower or "devops" in tech_lower or "iac" in tech_lower or "sre" in tech_lower:
return "devops-iac.md", "DevOps, IaC, and SRE"
elif "terraform" in tech_lower or "ansible" in tech_lower or "pulumi" in tech_lower or "crossplane" in tech_lower:
return "devops-iac.md", "DevOps, IaC, and SRE"
elif "gitops" in tech_lower or "argo" in tech_lower or "flux" in tech_lower or "tekton" in tech_lower or "jenkins" in tech_lower or "cicd" in tech_lower:
return "devops-iac.md", "DevOps, IaC, and SRE"
elif "prometheus" in tech_lower or "grafana" in tech_lower or "opentelemetry" in tech_lower or "otel" in tech_lower:
return "devops-iac.md", "DevOps, IaC, and SRE"
elif "finops" in tech_lower or "cost" in tech_lower or "kubecost" in tech_lower:
return "devops-iac.md", "DevOps, IaC, and SRE"
elif "fundamentals" in cat_lower or "certification" in tech_lower or "cka" in tech_lower or "training" in cat_lower:
return "fundamentals.md", "Fundamentals"
elif "aws" in tech_lower or "azure" in tech_lower or "gcp" in tech_lower or "google cloud" in tech_lower:
return "cloud-native.md", "Cloud Native Core"
elif "openshift" in tech_lower or "red hat" in tech_lower or "rancher" in tech_lower:
return "cloud-native.md", "Cloud Native Core"
elif "vmware" in tech_lower or "proxmox" in tech_lower or "virtualization" in tech_lower:
return "cloud-native.md", "Cloud Native Core"
elif "docker" in tech_lower or "container" in tech_lower or "podman" in tech_lower:
return "cloud-native.md", "Cloud Native Core"
elif "python" in tech_lower or "golang" in tech_lower or "java" in tech_lower or "javascript" in tech_lower:
return "fundamentals.md", "Fundamentals"
else:
return "cloud-native.md", "Cloud Native Core"
def generate_v2_videos():
if not os.path.exists(INVENTORY_PATH):
return
with open(INVENTORY_PATH, "r") as f:
inventory = yaml.safe_load(f)
from src.inventory_manager import load_inventory
inventory = load_inventory()
featured_videos = []
for url, entry in inventory.items():
+2 -2
View File
@@ -65,7 +65,7 @@ We prioritize established frameworks and enterprise standards over ad-hoc, unmai
#### 2.4. Avoiding Engineering Anti-Patterns
We combat the culture of **Promotion-Based Development (PBD)**, where complexity is manufactured for personal career visibility rather than business value.
- [Promotion-Based Development: A Fast Track to Mediocrity](https://vadimkravcenko.com/shorts/promotion-based-development/) <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Dissects how rewarding "shiny new things" over battle-tested stability leads to fragile architectures.
- [Promotion-Based Development: A Fast Track to Mediocrity](https://vadimkravcenko.com/shorts/promotion-based-development) <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Dissects how rewarding "shiny new things" over battle-tested stability leads to fragile architectures.
- [Reddit: The Reality of Promotion-Driven Development](https://www.reddit.com/r/ExperiencedDevs/comments/pw6vuv/promotion_driven_development) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A raw, evidence-based discussion from senior engineers on the industry's most common misaligned incentives.
### 3. The Architectural North Star
@@ -220,7 +220,7 @@ While certifications like CKA are prominent on CVs, they are frequently utilized
#### Terraform Boilerplates
??? note "Terraform Kubernetes Boilerplates 🌟"
**[Access Resource](https://nubenetes.com/terraform/)** 🌟🌟🌟🌟🌟 | Level: Advanced
**[Access Resource](https://nubenetes.com/terraform)** 🌟🌟🌟🌟🌟 | Level: Advanced
A library of enterprise-stable Terraform templates configured specifically for modern Kubernetes environments (EKS, GKE, AKS). Includes pre-tested infrastructure specifications for VPC topologies, private nodes, and dynamic ingress setups.
+1 -1
View File
@@ -161,7 +161,7 @@
#### AWS Templates
- **(2024)** [AWS Samples (Boilerplates)](https://nubenetes.com/demos/#aws-samples-boilerplates) <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A consolidated hub of official and community AWS deployment samples. Houses structured patterns and CloudFormation/Terraform codebases to fast-track prototype development in compliance with AWS architecture standards.
- **(2024)** [AWS Samples (Boilerplates)](https://nubenetes.com/demos) <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A consolidated hub of official and community AWS deployment samples. Houses structured patterns and CloudFormation/Terraform codebases to fast-track prototype development in compliance with AWS architecture standards.
---
💡 **Explore Related:** [Googlecloudplatform](./GoogleCloudPlatform.md) | [AWS Pricing](./aws-pricing.md) | [AWS Spain](./aws-spain.md)
+1 -1
View File
@@ -639,7 +639,7 @@
#### Network Cheat Sheets
- **(2022)** [Networking Cheat Sheet](https://nubenetes.com/networking/) <span class='md-tag md-tag--warning'>[N/A CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Consolidated technical reference detailing essential low-level networking parameters, CIDR calculations, subnetting concepts, and critical routing architectures. Serves as a quick diagnostic lookup sheet for standard network protocol analysis and port troubleshooting.
- **(2022)** [Networking Cheat Sheet](https://nubenetes.com/networking) <span class='md-tag md-tag--warning'>[N/A CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Consolidated technical reference detailing essential low-level networking parameters, CIDR calculations, subnetting concepts, and critical routing architectures. Serves as a quick diagnostic lookup sheet for standard network protocol analysis and port troubleshooting.
## Observability
### Metrics and Monitoring
+1 -1
View File
@@ -766,7 +766,7 @@
#### Crunchy PostgreSQL
- **(2023)** [Crunchy Data PostgreSQL Operator](https://nubenetes.com/crunchydata/) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Evaluates the Crunchy PostgreSQL Operator (PGO) which automates production-grade PostgreSQL deployments on Kubernetes. Features include automated high availability, pgBackRest-driven backup orchestration, connection pooling via pgBouncer, and deep monitoring metrics. A de facto standard solution for enterprises migrating critical relational engines into Kubernetes platforms.
- **(2023)** [Crunchy Data PostgreSQL Operator](https://nubenetes.com/crunchydata) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Evaluates the Crunchy PostgreSQL Operator (PGO) which automates production-grade PostgreSQL deployments on Kubernetes. Features include automated high availability, pgBackRest-driven backup orchestration, connection pooling via pgBouncer, and deep monitoring metrics. A de facto standard solution for enterprises migrating critical relational engines into Kubernetes platforms.
## Time-Series
### Architecture (1)
+1 -1
View File
@@ -2050,7 +2050,7 @@
#### EKS Training
- **(2025)** [eksworkshop.com](https://eksworkshop.com/) <span class='md-tag md-tag--warning'>[N/A CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — The canonical AWS EKS workshop framework. Outlines standard cluster orchestration procedures, highlighting network configurations (AWS VPC CNI), identity management (IAM Roles for Service Accounts - IRSA), and modern storage drivers (EBS/EFS CSI).
- **(2025)** [eksworkshop.com](https://eksworkshop.com) <span class='md-tag md-tag--warning'>[N/A CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — The canonical AWS EKS workshop framework. Outlines standard cluster orchestration procedures, highlighting network configurations (AWS VPC CNI), identity management (IAM Roles for Service Accounts - IRSA), and modern storage drivers (EBS/EFS CSI).
### Kubernetes Security
#### RKE Best Practices
+1 -1
View File
@@ -218,7 +218,7 @@
#### Keptn
- **(2026)** [**Keptn**](https://nubenetes.com/keptn/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Nubenetes architectural reference on Keptn, a CNCF enterprise-grade control plane for cloud-native application lifecycle orchestration. Integrates SLO-based evaluations, automated canary promotions, and zero-touch application remediation out of the box.
- **(2026)** [**Keptn**](https://nubenetes.com/keptn) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Nubenetes architectural reference on Keptn, a CNCF enterprise-grade control plane for cloud-native application lifecycle orchestration. Integrates SLO-based evaluations, automated canary promotions, and zero-touch application remediation out of the box.
---
💡 **Explore Related:** [Demos](./demos.md) | [Kubernetes](./kubernetes.md) | [Cloud Arch Diagrams](./cloud-arch-diagrams.md)
+3 -3
View File
@@ -523,7 +523,7 @@
#### Overview
- **(2026)** [**NoOps**](https://nubenetes.com/noops/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Comprehensive conceptual guide on NoOps (No Operations). Describes the strategic path to fully outsourcing infrastructure layers to automated platforms, serverless paradigms, and self-healing systems so engineering can focus 100% on application logic.
- **(2026)** [**NoOps**](https://nubenetes.com/noops) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Comprehensive conceptual guide on NoOps (No Operations). Describes the strategic path to fully outsourcing infrastructure layers to automated platforms, serverless paradigms, and self-healing systems so engineering can focus 100% on application logic.
### Serverless Systems
#### DevOps Pipelines
@@ -869,7 +869,7 @@
#### Overview (1)
- **(2026)** [**DevOps Tools**](https://nubenetes.com/devops-tools/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Catalog of modern DevOps tooling encompassing continuous integration, artifact storage, automated testing, container scheduling, and real-time telemetry pipelines to build stable, production-ready release processes.
- **(2026)** [**DevOps Tools**](https://nubenetes.com/devops-tools) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Catalog of modern DevOps tooling encompassing continuous integration, artifact storage, automated testing, container scheduling, and real-time telemetry pipelines to build stable, production-ready release processes.
## DevOps Methodology
### Application Delivery
@@ -1129,7 +1129,7 @@
#### Overview (2)
- **(2026)** [==IaC Infrastructure as Code==](https://nubenetes.com/iac/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Nubenetes architectural reference portal on Infrastructure as Code (IaC). Outlines fundamental philosophies, lifecycle management, and paradigm shifts of treating bare-metal, cloud, or cluster state as declarative, version-controlled code.
- **(2026)** [==IaC Infrastructure as Code==](https://nubenetes.com/iac) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Nubenetes architectural reference portal on Infrastructure as Code (IaC). Outlines fundamental philosophies, lifecycle management, and paradigm shifts of treating bare-metal, cloud, or cluster state as declarative, version-controlled code.
### Terraform
#### Entra ID Integration
+1 -1
View File
@@ -843,7 +843,7 @@
#### Docker Swarm
- **(2024)** [Docker Swarm](https://nubenetes.com/kubernetes-alternatives/#docker-swarm) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Analyzes Docker Swarm as a simple container orchestration alternative to Kubernetes. Evaluates its built-in overlay network routing mesh and single-node setup advantages, noting that its enterprise adoption has decreased in favor of Kubernetes-native environments.
- **(2024)** [Docker Swarm](https://nubenetes.com/kubernetes-alternatives) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Analyzes Docker Swarm as a simple container orchestration alternative to Kubernetes. Evaluates its built-in overlay network routing mesh and single-node setup advantages, noting that its enterprise adoption has decreased in favor of Kubernetes-native environments.
## Performance
### Diagnostics (1)
+171
View File
@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Nubenetes Intelligence Digest</title>
<link>https://nubenetes.com/</link>
<description>AI-curated top picks from the Cloud Native &amp; Kubernetes ecosystem</description>
<language>en</language>
<lastBuildDate>Fri, 19 Jun 2026 10:51:12 -0000</lastBuildDate>
<atom:link href="https://nubenetes.com/feed.xml" rel="self" type="application/rss+xml"/>
<item>
<title>antigravity.google: Google Antigravity Agentic Platform</title>
<link>https://antigravity.google</link>
<guid isPermaLink="true">https://antigravity.google</guid>
<pubDate>Thu, 18 Jun 2026 00:00:00 -0000</pubDate>
<category>AI &amp; Agents</category>
<description>[CRITICAL] It provides an SDK and platform specifically designed to transition stateful AI agents from local development to secure GKE production environments.</description>
</item>
<item>
<title>docs.anthropic.com: Claude Code CLI</title>
<link>https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview</link>
<guid isPermaLink="true">https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview</guid>
<pubDate>Thu, 18 Jun 2026 00:00:00 -0000</pubDate>
<category>AI &amp; Agents</category>
<description>[CRITICAL] This official CLI tool from Anthropic introduces highly autonomous agentic engineering capabilities directly into local developer environments.</description>
</item>
<item>
<title>Crossplane</title>
<link>https://nubenetes.com/crossplane</link>
<guid isPermaLink="true">https://nubenetes.com/crossplane</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>Kubernetes &amp; Orchestration</category>
<description>[CRITICAL] It transforms Kubernetes into a universal control plane, allowing teams to manage external cloud infrastructure alongside containerized workloads.</description>
</item>
<item>
<title>NVIDIA/k8s-device-plugin: NVIDIA device plugin for Kubernetes</title>
<link>https://github.com/NVIDIA/k8s-device-plugin</link>
<guid isPermaLink="true">https://github.com/NVIDIA/k8s-device-plugin</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>Kubernetes &amp; Orchestration</category>
<description>[CRITICAL] It acts as the essential orchestrator bridging physical GPU capabilities to workloads, a prerequisite for modern AI/ML execution on Kubernetes.</description>
</item>
<item>
<title>vLLM on Kubernetes</title>
<link>https://github.com/vllm-project/vllm</link>
<guid isPermaLink="true">https://github.com/vllm-project/vllm</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>AI &amp; Agents</category>
<description>[CRITICAL] It standardizes memory-efficient LLM serving with vLLM directly on Kubernetes, bridging cloud-native orchestration with state-of-the-art AI inference.</description>
</item>
<item>
<title>Tetragon (Cilium)</title>
<link>https://github.com/cilium/tetragon</link>
<guid isPermaLink="true">https://github.com/cilium/tetragon</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>Security &amp; Compliance</category>
<description>[CRITICAL] Tetragon leverages eBPF to deliver low-overhead, kernel-level runtime enforcement and security observability directly tailored for cloud-native workloads.</description>
</item>
<item>
<title>Helm</title>
<link>https://nubenetes.com/helm</link>
<guid isPermaLink="true">https://nubenetes.com/helm</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>CI/CD &amp; GitOps</category>
<description>[CRITICAL] Helm is the undisputed package manager for Kubernetes, serving as the core template and packaging engine for cloud-native deployments.</description>
</item>
<item>
<title>==cert-manager/cert-manager==</title>
<link>https://github.com/cert-manager/cert-manager</link>
<guid isPermaLink="true">https://github.com/cert-manager/cert-manager</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>Containers &amp; Runtime</category>
<description>[CRITICAL] This project is the undisputed cloud-native standard for automating TLS/SSL certificate lifecycle management in Kubernetes clusters.</description>
</item>
<item>
<title>github.com: Istio</title>
<link>https://github.com/istio/istio</link>
<guid isPermaLink="true">https://github.com/istio/istio</guid>
<pubDate>Sun, 14 Jun 2026 00:00:00 -0000</pubDate>
<category>Networking &amp; Service Mesh</category>
<description>[CRITICAL] As the industry-standard enterprise service mesh, Istio provides the critical infrastructure for secure, observable, and resilient microservice communications.</description>
</item>
<item>
<title>github: Flux Version 2</title>
<link>https://github.com/fluxcd/flux2</link>
<guid isPermaLink="true">https://github.com/fluxcd/flux2</guid>
<pubDate>Sat, 13 Jun 2026 00:00:00 -0000</pubDate>
<category>CI/CD &amp; GitOps</category>
<description>[CRITICAL] Flux v2 is a foundational CNCF graduated project implementing highly parallel, secure GitOps reconciliation controllers.</description>
</item>
<item>
<title>github.com/prometheus/prometheus</title>
<link>https://github.com/prometheus/prometheus</link>
<guid isPermaLink="true">https://github.com/prometheus/prometheus</guid>
<pubDate>Sat, 13 Jun 2026 00:00:00 -0000</pubDate>
<category>Observability, SRE &amp; Testing</category>
<description>[CRITICAL] Prometheus is the de facto standard metric engine for cloud-native ecosystems, providing critical sub-second query speeds and robust local storage.</description>
</item>
<item>
<title>containerd - An open and reliable container runtime</title>
<link>https://github.com/containerd/containerd</link>
<guid isPermaLink="true">https://github.com/containerd/containerd</guid>
<pubDate>Sat, 13 Jun 2026 00:00:00 -0000</pubDate>
<category>Containers &amp; Runtime</category>
<description>[CRITICAL] As the de facto industry-standard container runtime for Kubernetes, containerd is critical for running enterprise cloud-native workloads.</description>
</item>
<item>
<title>runc</title>
<link>https://github.com/opencontainers/runc</link>
<guid isPermaLink="true">https://github.com/opencontainers/runc</guid>
<pubDate>Sat, 13 Jun 2026 00:00:00 -0000</pubDate>
<category>Containers &amp; Runtime</category>
<description>[CRITICAL] Runc is the foundational, low-level OCI-compliant runtime that interfaces directly with the Linux kernel to execute modern containers.</description>
</item>
<item>
<title>==github.com/Netflix/metaflow== 🌟</title>
<link>https://github.com/Netflix/metaflow</link>
<guid isPermaLink="true">https://github.com/Netflix/metaflow</guid>
<pubDate>Sat, 13 Jun 2026 00:00:00 -0000</pubDate>
<category>MLOps &amp; Data Science</category>
<description>[CRITICAL] Metaflow bridges local development and cloud infrastructure, simplifying production-grade data science pipeline execution at scale.</description>
</item>
<item>
<title>hashicorp/vault</title>
<link>https://github.com/hashicorp/vault</link>
<guid isPermaLink="true">https://github.com/hashicorp/vault</guid>
<pubDate>Fri, 12 Jun 2026 00:00:00 -0000</pubDate>
<category>Security &amp; Compliance</category>
<description>[CRITICAL] HashiCorp Vault remains the foundational, enterprise-grade standard for securing dynamic secrets and implementing zero-trust architectures across multi-cloud environments.</description>
</item>
<item>
<title>OpenTelemetry Collector</title>
<link>https://github.com/open-telemetry/opentelemetry-collector</link>
<guid isPermaLink="true">https://github.com/open-telemetry/opentelemetry-collector</guid>
<pubDate>Fri, 12 Jun 2026 00:00:00 -0000</pubDate>
<category>Observability, SRE &amp; Testing</category>
<description>[CRITICAL] OpenTelemetry Collector acts as the universal, high-performance telemetry pipeline for enterprise observability, unifying logs, metrics, and traces.</description>
</item>
<item>
<title>Kubernetes Gateway API</title>
<link>https://github.com/kubernetes-sigs/gateway-api</link>
<guid isPermaLink="true">https://github.com/kubernetes-sigs/gateway-api</guid>
<pubDate>Fri, 12 Jun 2026 00:00:00 -0000</pubDate>
<category>Networking &amp; Service Mesh</category>
<description>[CRITICAL] The Gateway API represents a major paradigm shift, replacing legacy Ingress with an expressive, role-oriented, and extensible routing standard.</description>
</item>
<item>
<title>github.com/vmware-tanzu/velero</title>
<link>https://github.com/velero-io/velero</link>
<guid isPermaLink="true">https://github.com/velero-io/velero</guid>
<pubDate>Fri, 12 Jun 2026 00:00:00 -0000</pubDate>
<category>Data, Messaging &amp; Storage</category>
<description>[CRITICAL] It is the industry-standard open-source tool for Kubernetes cluster backup, disaster recovery, and stateful volume migration.</description>
</item>
<item>
<title>trivy</title>
<link>https://github.com/aquasecurity/trivy</link>
<guid isPermaLink="true">https://github.com/aquasecurity/trivy</guid>
<pubDate>Thu, 11 Jun 2026 00:00:00 -0000</pubDate>
<category>Security &amp; Compliance</category>
<description>[CRITICAL] Trivy represents the industry standard for fast, comprehensive security scanning of container images, Kubernetes configurations, and software bills of materials.</description>
</item>
<item>
<title>OpenTofu 1.12: the Feature Terraform Never Shipped</title>
<link>https://www.infoq.com/news/2026/05/opentofu-release-terraform</link>
<guid isPermaLink="true">https://www.infoq.com/news/2026/05/opentofu-release-terraform</guid>
<pubDate>Tue, 02 Jun 2026 00:00:00 -0000</pubDate>
<category>Infrastructure as Code</category>
<description>[CRITICAL] This landmark OpenTofu release solves a decade-old limitation of upstream Terraform by enabling the pre-evaluation of providers using variables.</description>
</item>
</channel>
</rss>
+1 -1
View File
@@ -1074,7 +1074,7 @@ Guides developers on configuring autonomous multi-file refactoring, debugging, a
- **(2022)** [grafana: How we use the Grafana GitHub plugin to track outstanding pull requests](https://grafana.com/blog/how-we-use-the-grafana-github-plugin-to-track-outstanding-pull-requests) <span class='md-tag md-tag--secondary'>[CASE STUDY]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Detailed technical guide on configuring Grafana dashboards with GitHub plugins. Demonstrates building engineering performance visualizations to track commit frequencies, PR lifetimes, and team review velocities.
#### VS Code Extensions
- **(2025)** [Visual Studio Code (Git Extensions)](https://nubenetes.com/visual-studio/) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A guide to utilizing visual Git extensions inside VS Code. Helps developers manage commit sequences, visualize branch merges, and resolve conflicts within a unified IDE workspace.
- **(2025)** [Visual Studio Code (Git Extensions)](https://nubenetes.com/visual-studio) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A guide to utilizing visual Git extensions inside VS Code. Helps developers manage commit sequences, visualize branch merges, and resolve conflicts within a unified IDE workspace.
### Documentation (2)
#### Markup Languages
+2 -2
View File
@@ -289,10 +289,10 @@
- **(2021)** [ibm.com: Enable GitOps](https://www.ibm.com/garage) <span class='md-tag md-tag--warning'>[N/A CONTENT]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> <span class='md-tag md-tag--critical'>[LEGACY]</span> — An enterprise change-management guide from IBM Garage focusing on GitOps adoption. Details organizational processes, environment categorization, and verification configurations required to transition legacy pipelines into declarative GitOps models.
#### FluxCD
- **(2025)** [Flux. The GitOps operator for Kubernetes](https://nubenetes.com/flux/) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — The main technical documentation and resources for Flux, the CNCF-graduated continuous delivery tool for Kubernetes. Analyzes multi-tenancy configurations, automated image update policies, and source controller optimizations that make Flux a core component of modern GitOps workflows.
- **(2025)** [Flux. The GitOps operator for Kubernetes](https://nubenetes.com/flux) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — The main technical documentation and resources for Flux, the CNCF-graduated continuous delivery tool for Kubernetes. Analyzes multi-tenancy configurations, automated image update policies, and source controller optimizations that make Flux a core component of modern GitOps workflows.
#### Kustomize Manifests
- **(2025)** [Kustomize - Template-Free Kubernetes Configuration Customization](https://nubenetes.com/kustomize/) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Technical reference for Kustomize, the template-free engine used to manage Kubernetes configurations. Details declarative base and overlay architectures, allowing developers to manage configurations for different environments (dev, staging, prod) without using complex Helm template structures.
- **(2025)** [Kustomize - Template-Free Kubernetes Configuration Customization](https://nubenetes.com/kustomize) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Technical reference for Kustomize, the template-free engine used to manage Kubernetes configurations. Details declarative base and overlay architectures, allowing developers to manage configurations for different environments (dev, staging, prod) without using complex Helm template structures.
## Cloud Infrastructure
### Infrastructure as Code (1)
+12 -5
View File
@@ -1,4 +1,4 @@
# Nubenetes Elite Portal (V2) | Awesome Kubernetes & Cloud [![Awesome](https://cdn.jsdelivr.net/gh/sindresorhus/awesome@d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)
# Nubenetes Elite Portal (V2) | Awesome Kubernetes and Cloud [![Awesome](https://cdn.jsdelivr.net/gh/sindresorhus/awesome@d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)
!!! tip "Nubenetes V2 Elite Portal: AI-Curated & High-Density"
You are browsing the AI-Curated V2 Elite Edition of Nubenetes. Looking for the complete historical archive? Explore the [**V1 Historical Archive**](/v1/).
@@ -39,6 +39,13 @@
<div class="hero-badge-subtitle">Agentic Ecosystem</div>
</div>
</a>
<a href="./tech-digest/" style="text-decoration: none; color: inherit; display: block;">
<div class="hero-badge-card hero-badge-card--amber">
<div class="hero-badge-icon">📊</div>
<div class="hero-badge-title">Intelligence Digest</div>
<div class="hero-badge-subtitle">Top picks · 3/6/12 months</div>
</div>
</a>
<a href="./videos/" style="text-decoration: none; color: inherit; display: block;">
<div class="hero-badge-card hero-badge-card--pink">
<img src="/images/video_hub_logo.png" alt="Agentic Video Hub"/>
@@ -153,7 +160,7 @@
- **[Monitoring](./monitoring.md)**
- **[Other Awesome Lists](./other-awesome-lists.md)**
- **[Prometheus](./prometheus.md)**
### Platform & Site Reliability
### Platform and Site Reliability
- **[Chaos Engineering](./chaos-engineering.md)**
- **[Developerportals](./developerportals.md)**
- **[DevOps](./devops.md)**
@@ -209,7 +216,7 @@
- **[Private Cloud Solutions](./private-cloud-solutions.md)**
- **[Public Cloud Solutions](./public-cloud-solutions.md)**
- **[Scaleway](./scaleway.md)**
### Networking & Service Mesh
### Networking and Service Mesh
- **[Caching](./caching.md)**
- **[Cloudflare](./cloudflare.md)**
- **[Istio](./istio.md)**
@@ -239,7 +246,7 @@
- **[Openshift](./openshift.md)**
- **[Rancher](./rancher.md)**
- **[Serverless](./serverless.md)**
### Data & Advanced Analytics
### Data and Advanced Analytics
- **[Crunchydata](./crunchydata.md)**
- **[Databases](./databases.md)**
- **[Message Queue](./message-queue.md)**
@@ -284,7 +291,7 @@
- **[Visual Studio](./visual-studio.md)**
- **[Web3](./web3.md)**
- **[Xamarin](./xamarin.md)**
### Career & Industry
### Career and Industry
- **[Appointment Scheduling](./appointment-scheduling.md)**
- **[Digital Money](./digital-money.md)**
- **[Elearning](./elearning.md)**
+14
View File
@@ -0,0 +1,14 @@
# Nubenetes Industry and Geo Intelligence Digest
!!! tip "Nubenetes Intelligence Digest"
AI-curated ranking of the most impactful resources, updated monthly.
=== "Last 3 Months"
=== "Last 6 Months"
=== "Last 12 Months"
+1 -1
View File
@@ -204,7 +204,7 @@
#### Tekton Pipelines
- **(2023)** [Tekton](https://nubenetes.com/tekton/) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Deep-dive review of Tekton, a Kubernetes-native open-source framework for building continuous integration and delivery (CI/CD) pipelines. It structures pipeline blocks using standard CRDs (Tasks, Pipelines, PipelineRuns), eliminating VM-based runner dependencies. Live validation establishes Tekton as the standard engine powering modern cloud-native container build environments like OpenShift Pipelines.
- **(2023)** [Tekton](https://nubenetes.com/tekton) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Deep-dive review of Tekton, a Kubernetes-native open-source framework for building continuous integration and delivery (CI/CD) pipelines. It structures pipeline blocks using standard CRDs (Tasks, Pipelines, PipelineRuns), eliminating VM-based runner dependencies. Live validation establishes Tekton as the standard engine powering modern cloud-native container build environments like OpenShift Pipelines.
#### Tool Comparison
- **(2021)** [k21academy.com: Azure pipelines VS Jenkins](https://k21academy.com/azure-cloud/azure-pipelines-vs-jenkins) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A comprehensive comparative analysis contrasting Jenkins (self-hosted, highly extensible, plugin-heavy) against Azure Pipelines (managed, cloud-native SaaS, deep Azure integration). Highlights differences in maintenance overhead, security configurations, build agent execution, and enterprise scaling architectures. Essential reading for platform teams deciding on their continuous delivery stack.
+3 -3
View File
@@ -149,7 +149,7 @@
#### Gradle Reference
- **(2026)** [==Gradle Cheat Sheets==](https://nubenetes.com/cheatsheets/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — High-density command syntax cheatsheet for Gradle, highlighting Kotlin/Groovy DSL setups, caching options, task graphs management, and daemon management to significantly improve build execution times.
- **(2026)** [==Gradle Cheat Sheets==](https://nubenetes.com/cheatsheets) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — High-density command syntax cheatsheet for Gradle, highlighting Kotlin/Groovy DSL setups, caching options, task graphs management, and daemon management to significantly improve build execution times.
## Developer Experience
### Shell
@@ -168,7 +168,7 @@
#### Kubectl Plugins
- **(2025)** [Kubectl plugins and tools](https://nubenetes.com/kubernetes/#kubectl-plugins) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — This reference compilation highlights external tools and kubectl extensions managed via Krew. It details how third-party plugins (like `neat`, `kns`, or security-focused extensions) expand basic kubectl operational debugging and cluster-inspection capabilities.
- **(2025)** [Kubectl plugins and tools](https://nubenetes.com/kubernetes) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — This reference compilation highlights external tools and kubectl extensions managed via Krew. It details how third-party plugins (like `neat`, `kns`, or security-focused extensions) expand basic kubectl operational debugging and cluster-inspection capabilities.
#### Productivity (1)
- **(2021)** [Kubernetes productivity tips and tricks 🌟](https://www.theodo.com/en-fr/blog) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A practitioner's guide to enhancing CLI-based Kubernetes productivity. It explores advanced setups such as custom shell autocompletion, kubectx/kubens utilities, smart aliases, and log-tailing helpers designed to reduce cognitive overhead during real-time incident responses.
@@ -242,7 +242,7 @@
#### Helm Overview
- **(2026)** [==Helm==](https://nubenetes.com/helm/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Deep-dive architecture portal on Helm, the package manager for Kubernetes. Focuses on structuring dry templates, lifecycle hooks, chart dependencies, release versioning, and secure variables management inside GitOps pipelines.
- **(2026)** [==Helm==](https://nubenetes.com/helm) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Deep-dive architecture portal on Helm, the package manager for Kubernetes. Focuses on structuring dry templates, lifecycle hooks, chart dependencies, release versioning, and secure variables management inside GitOps pipelines.
## Storage and Data
### Performance Benchmarking
+2 -2
View File
@@ -253,12 +253,12 @@
#### Multi-Cluster
- **(2026)** [Rancher: Enterprise management for Kubernetes](https://nubenetes.com/rancher/) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Rancher is a unified platform for managing multi-cluster, heterogeneous Kubernetes deployments across diverse cloud providers and bare metal hosts. It simplifies operational management by providing centralized authentication, unified RBAC access, structured audit logs, and simplified Helm catalog deployments.
- **(2026)** [Rancher: Enterprise management for Kubernetes](https://nubenetes.com/rancher) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Rancher is a unified platform for managing multi-cluster, heterogeneous Kubernetes deployments across diverse cloud providers and bare metal hosts. It simplifies operational management by providing centralized authentication, unified RBAC access, structured audit logs, and simplified Helm catalog deployments.
### Red Hat OpenShift
#### Enterprise (1)
- **(2026)** [Openshift Container Platform](https://nubenetes.com/openshift/) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Red Hat OpenShift is a premier enterprise-grade hybrid cloud Kubernetes application platform. It adds out-of-the-box developer tooling, integrated security standards, cluster virtualization, internal registry configurations, and Operator-based life cycle management directly over raw Kubernetes.
- **(2026)** [Openshift Container Platform](https://nubenetes.com/openshift) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Red Hat OpenShift is a premier enterprise-grade hybrid cloud Kubernetes application platform. It adds out-of-the-box developer tooling, integrated security standards, cluster virtualization, internal registry configurations, and Operator-based life cycle management directly over raw Kubernetes.
## Networking
### CNI Plugins
+4 -4
View File
@@ -1321,7 +1321,7 @@
#### Overview
- **(2026)** [==Serverless Architectures==](https://nubenetes.com/serverless/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — In-depth analysis exploring execution concepts, billing architectures, scalability curves, and performance tradeoffs inherent in Serverless patterns. Details key differences between FaaS, cloud-managed runtimes, and self-hosted Knative workloads.
- **(2026)** [==Serverless Architectures==](https://nubenetes.com/serverless) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — In-depth analysis exploring execution concepts, billing architectures, scalability curves, and performance tradeoffs inherent in Serverless patterns. Details key differences between FaaS, cloud-managed runtimes, and self-hosted Knative workloads.
## Cloud Infrastructure
### Kubernetes (1)
@@ -1585,7 +1585,7 @@
#### API Client Libraries
- **(2026)** [==Client Libraries for Kubernetes==](https://nubenetes.com/kubernetes-client-libraries/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Complete directory of supported Kubernetes API client libraries (Python, Go, Java, JavaScript, etc.). Details patterns for programmatic service discovery, controller building, and custom automation direct from application runtime code.
- **(2026)** [==Client Libraries for Kubernetes==](https://nubenetes.com/kubernetes-client-libraries) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Complete directory of supported Kubernetes API client libraries (Python, Go, Java, JavaScript, etc.). Details patterns for programmatic service discovery, controller building, and custom automation direct from application runtime code.
### Kubernetes Operations
#### Pod Restarts
@@ -1595,7 +1595,7 @@
#### Volumes Overview
- **(2026)** [==Kubernetes Storage - Volumes==](https://nubenetes.com/kubernetes-storage/#kubernetes-volumes) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Detailed catalog explaining stateful execution patterns inside Kubernetes. Focuses on lifecycle dynamics of Ephemeral, Persistent (PV), and PersistentVolumeClaims (PVC), alongside container storage interfaces (CSI) used to integrate modern storage backends.
- **(2026)** [==Kubernetes Storage - Volumes==](https://nubenetes.com/kubernetes-storage) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Detailed catalog explaining stateful execution patterns inside Kubernetes. Focuses on lifecycle dynamics of Ephemeral, Persistent (PV), and PersistentVolumeClaims (PVC), alongside container storage interfaces (CSI) used to integrate modern storage backends.
### Virtualization VS Containers
#### Kubernetes Concept
@@ -2145,7 +2145,7 @@
#### Overview (1)
- **(2026)** [==Crossplane==](https://nubenetes.com/crossplane/) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Comprehensive review of Crossplane, a CNCF control-plane framework transforming Kubernetes clusters into universal infrastructure schedulers. Permits declarative definition of cloud resources (RDS, S3, VMs) alongside native Kubernetes schemas.
- **(2026)** [==Crossplane==](https://nubenetes.com/crossplane) <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — Comprehensive review of Crossplane, a CNCF control-plane framework transforming Kubernetes clusters into universal infrastructure schedulers. Permits declarative definition of cloud resources (RDS, S3, VMs) alongside native Kubernetes schemas.
## Introductory
### Concepts (1)
+2 -2
View File
@@ -747,7 +747,7 @@
#### Metrics Collection
- **(2024)** [Prometheus](https://nubenetes.com/prometheus/) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Prometheus is an open-source systems monitoring and alerting toolkit originally built at SoundCloud. It utilizes a pull-based metrics collection model over HTTP, powered by a highly efficient dimensional data model (TSDB) with PromQL. Essential for Kubernetes cloud-native environments, it excels in dynamic service discovery and real-time operational visibility.
- **(2024)** [Prometheus](https://nubenetes.com/prometheus) <span class='md-tag md-tag--warning'>[GO CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Prometheus is an open-source systems monitoring and alerting toolkit originally built at SoundCloud. It utilizes a pull-based metrics collection model over HTTP, powered by a highly efficient dimensional data model (TSDB) with PromQL. Essential for Kubernetes cloud-native environments, it excels in dynamic service discovery and real-time operational visibility.
### OpenTelemetry (1)
#### Collector Infrastructure
@@ -789,7 +789,7 @@
#### Dashboards
- **(2024)** [Grafana](https://nubenetes.com/grafana/) <span class='md-tag md-tag--warning'>[GO/TYPESCRIPT CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Grafana is the industry-standard multi-platform open-source analytics and interactive visualization web application. It supports query, visualization, alerting, and analysis of metrics, logs, and traces from diverse backends (Prometheus, Elasticsearch, Loki, Jaeger). Its pluggable architecture allows organizations to build unified operational dashboards across heterogeneous data layers.
- **(2024)** [Grafana](https://nubenetes.com/grafana) <span class='md-tag md-tag--warning'>[GO/TYPESCRIPT CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Grafana is the industry-standard multi-platform open-source analytics and interactive visualization web application. It supports query, visualization, alerting, and analysis of metrics, logs, and traces from diverse backends (Prometheus, Elasticsearch, Loki, Jaeger). Its pluggable architecture allows organizations to build unified operational dashboards across heterogeneous data layers.
## Observability and Monitoring
### Application Performance Monitoring (1)
+2 -2
View File
@@ -343,7 +343,7 @@
#### Red Hat Quay
- **(2022)** [OpenShift Registry & Quay](https://nubenetes.com/registries/) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Comprehensive analysis of Red Hat Quay and the integrated OpenShift Container Registry. Details secure image storage, vulnerability scanning with Clair, and geo-replication capabilities. It highlights Quay's enterprise-grade multi-tenancy and RBAC controls, which ensure secure artifact promotion within high-performance microservices pipelines.
- **(2022)** [OpenShift Registry & Quay](https://nubenetes.com/registries) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> — Comprehensive analysis of Red Hat Quay and the integrated OpenShift Container Registry. Details secure image storage, vulnerability scanning with Clair, and geo-replication capabilities. It highlights Quay's enterprise-grade multi-tenancy and RBAC controls, which ensure secure artifact promotion within high-performance microservices pipelines.
## CI-CD
### App Migration (1)
@@ -431,7 +431,7 @@
- **(2019)** [cloudowski.com: Openshift ImageStreams](https://cloudowski.com/articles/why-managing-container-images-on-openshift-is-better-than-on-kubernetes) <span class='md-tag md-tag--warning'>[N/A CONTENT]</span> <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — This analysis compares OpenShift ImageStreams with vanilla Kubernetes image pulling mechanisms. It explains how ImageStreams provide abstraction layers over container registries, enabling automatic redeployments upon detection of updated remote images (triggers). By decoupling pods from concrete repository URLs, it automates deployment lifecycle workflows for platform engineering teams.
## Container Platforms
### OKD OpenShift
### OKD OpenShift
#### Cluster Bootstrap
+1 -1
View File
@@ -87,7 +87,7 @@
#### General Reference
- [uncontained.io/articles/openshift-ha-installation](https://uncontained.io/articles/openshift-ha-installation/) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A curated technical resource and architectural guide covering uncontained.io in the Kubernetes Tools ecosystem.
- [uncontained.io/articles/openshift-ha-installation](https://uncontained.io/articles/openshift-ha-installation) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A curated technical resource and architectural guide covering uncontained.io in the Kubernetes Tools ecosystem.
- [aroworkshop.io 🌟](https://aroworkshop.io) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A curated technical resource and architectural guide covering aroworkshop.io in the Kubernetes Tools ecosystem.
- [O'Reilly Free Book: **Openshift for developers**](https://www.redhat.com/en/technologies/cloud-computing/openshift/for-developers) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A curated technical resource and architectural guide covering www.redhat.com in the Kubernetes Tools ecosystem.
- [NetworkPolicies and Microsegmentation](https://www.redhat.com/en/blog/channel/hybrid-cloud-infrastructure/networkpolicies-and-microsegmentation) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A curated technical resource and architectural guide covering www.redhat.com in the Kubernetes Tools ecosystem.
+1 -1
View File
@@ -48,7 +48,7 @@
#### Java Ecosystem
- **(2025)** [Maven](https://nubenetes.com/maven-gradle/) <span class='md-tag md-tag--warning'>[JAVA CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Comparative architectural overview of Maven and Gradle. Outlines declarative XML configurations versus programmatic Groovy/Kotlin Gradle DSL scripts, analyzing cache efficiency, parallel build runtimes, and enterprise dependency-resolution policies.
- **(2025)** [Maven](https://nubenetes.com/maven-gradle) <span class='md-tag md-tag--warning'>[JAVA CONTENT]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — Comparative architectural overview of Maven and Gradle. Outlines declarative XML configurations versus programmatic Groovy/Kotlin Gradle DSL scripts, analyzing cache efficiency, parallel build runtimes, and enterprise dependency-resolution policies.
---
💡 **Explore Related:** [DevOps](./devops.md) | [Developerportals](./developerportals.md) | [SRE](./sre.md)
+1 -1
View File
@@ -240,7 +240,7 @@
#### Cloud Governance
- **(2026)** [**Azure Policy**](https://nubenetes.com/azure/) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Specialized gateway and reference documentation for enforcing structural compliance, resource auditing, and governance across Azure resource environments. Explains custom definition policies, policy initiatives, and automated remediation workflows. Critical reference for maintaining operational guardrails in enterprise cloud architectures.
- **(2026)** [**Azure Policy**](https://nubenetes.com/azure) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--primary'>[DOCUMENTATION]</span> 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — Specialized gateway and reference documentation for enforcing structural compliance, resource auditing, and governance across Azure resource environments. Explains custom definition policies, policy initiatives, and automated remediation workflows. Critical reference for maintaining operational guardrails in enterprise cloud architectures.
## Security
### Policy Enforcement
+1 -1
View File
@@ -134,7 +134,7 @@
#### Istio
- **(2026)** [Istio](https://nubenetes.com/istio/) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A comprehensive entry point to Istio architecture, the enterprise-grade service mesh. Details how engineers manage traffic routes, secure service-to-service communication with mutual TLS, and gain deep tracing observability across distributed Kubernetes deployments.
- **(2026)** [Istio](https://nubenetes.com/istio) <span class='md-tag md-tag--critical'>[ADVANCED LEVEL]</span> <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> — A comprehensive entry point to Istio architecture, the enterprise-grade service mesh. Details how engineers manage traffic routes, secure service-to-service communication with mutual TLS, and gain deep tracing observability across distributed Kubernetes deployments.
## Cloud Native Infrastructure
### API Management
+20 -20
View File
@@ -174,7 +174,7 @@
- **(2026)** [==github.com/backstage/backstage==](https://github.com/backstage/backstage) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[TYPESCRIPT CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [==How-To Secure A Linux Server==](https://github.com/imthenachoman/How-To-Secure-A-Linux-Server) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SHELL CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [==IaC Infrastructure as Code==](https://nubenetes.com/iac/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [==IaC Infrastructure as Code==](https://nubenetes.com/iac) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [==bregman-arie/devops-exercises 🌟==](https://github.com/bregman-arie/devops-exercises) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--secondary'>[GUIDE]</span> <span class='md-tag md-tag--warning'>[PYTHON/YAML CONTENT]</span> — *Go to [Section](./demos.md)*
- **(2026)** [==Developer Sandbox==](https://developers.redhat.com/developer-sandbox) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — *Go to [Section](./demos.md)*
- **(2026)** [==github: Spring Cloud Kubernetes 🌟==](https://github.com/spring-cloud/spring-cloud-kubernetes) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[JAVA CONTENT]</span> — *Go to [Section](./demos.md)*
@@ -202,15 +202,15 @@
- **(2026)** [==github.com/sharadbhat/KubernetesPatterns: YAML and Golang implementations' of common Kubernetes patterns==](https://github.com/sharadbhat/KubernetesPatterns) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==kustomize==](https://github.com/kubernetes-sigs/kustomize) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==jsonnet data templating language==](https://github.com/google/jsonnet/tree/master/case_studies/kubernetes) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[JSONNET CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Kubernetes Storage - Volumes==](https://nubenetes.com/kubernetes-storage/#kubernetes-volumes) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Client Libraries for Kubernetes==](https://nubenetes.com/kubernetes-client-libraries/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Kubernetes Storage - Volumes==](https://nubenetes.com/kubernetes-storage) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Client Libraries for Kubernetes==](https://nubenetes.com/kubernetes-client-libraries) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==kubectl-trace==](https://github.com/iovisor/kubectl-trace) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==kubespy==](https://github.com/huazhihao/kubespy) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==kubectl netshoot==](https://github.com/nilic/kubectl-netshoot) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==kubernetes.io: Kubernetes API==](https://kubernetes.io/docs/reference/kubernetes-api) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Crossplane==](https://nubenetes.com/crossplane/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Crossplane==](https://nubenetes.com/crossplane) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==davidB/kubectl-view-allocations==](https://github.com/davidB/kubectl-view-allocations) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[RUST CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Serverless Architectures==](https://nubenetes.com/serverless/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Serverless Architectures==](https://nubenetes.com/serverless) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Pulumi: Infrastructure as Code in Any Programming Language==](https://github.com/pulumi/pulumi) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./iac.md)*
- **(2026)** [==Awesome Sysadmin==](https://github.com/awesome-foss/awesome-sysadmin) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[MARKDOWN CONTENT]</span> — *Go to [Section](./devops-tools.md)*
- **(2026)** [==github.com/hashicorp/hcl: HCL==](https://github.com/hashicorp/hcl) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./terraform.md)*
@@ -287,8 +287,8 @@
- **(2026)** [==**GitHub build-push-action**==](https://github.com/docker/build-push-action) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[TYPESCRIPT CONTENT]</span> — *Go to [Section](./docker.md)*
- **(2026)** [**googlecloudcheatsheet.withgoogle.com: Google Cloud Developer cheat sheet**](https://cloud.google.com/products) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[HTML CONTENT]</span> — *Go to [Section](./GoogleCloudPlatform.md)*
- **(2026)** [**DevOps Tools**](https://nubenetes.com/devops-tools/) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**NoOps**](https://nubenetes.com/noops/) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**DevOps Tools**](https://nubenetes.com/devops-tools) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**NoOps**](https://nubenetes.com/noops) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**redhatgov.io**](https://redhatgov.io) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> — *Go to [Section](./demos.md)*
- **(2026)** [**Terraform & OpenTofu Skill for AI Agents**](https://github.com/antonbabenko/terraform-skill) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[TYPESCRIPT CONTENT]</span> — *Go to [Section](./cicd.md)*
- **(2026)** [**Nelm: A Helm Alternative for Kubernetes Deployments**](https://github.com/werf/nelm) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[GO CONTENT]</span> — *Go to [Section](./gitops.md)*
@@ -932,23 +932,23 @@
<details markdown="1">
<summary>Click to view top 100 of 212 resources under Spanish Content</summary>
- **(2026)** [==IaC Infrastructure as Code==](https://nubenetes.com/iac/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [==Kubernetes Storage - Volumes==](https://nubenetes.com/kubernetes-storage/#kubernetes-volumes) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Client Libraries for Kubernetes==](https://nubenetes.com/kubernetes-client-libraries/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Crossplane==](https://nubenetes.com/crossplane/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Serverless Architectures==](https://nubenetes.com/serverless/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Gradle Cheat Sheets==](https://nubenetes.com/cheatsheets/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubectl-commands.md)*
- **(2026)** [==Helm==](https://nubenetes.com/helm/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubectl-commands.md)*
- **(2026)** [==IaC Infrastructure as Code==](https://nubenetes.com/iac) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [==Kubernetes Storage - Volumes==](https://nubenetes.com/kubernetes-storage) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Client Libraries for Kubernetes==](https://nubenetes.com/kubernetes-client-libraries) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Crossplane==](https://nubenetes.com/crossplane) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Serverless Architectures==](https://nubenetes.com/serverless) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubernetes.md)*
- **(2026)** [==Gradle Cheat Sheets==](https://nubenetes.com/cheatsheets) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubectl-commands.md)*
- **(2026)** [==Helm==](https://nubenetes.com/helm) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./kubectl-commands.md)*
- **(2026)** [==codely.tv==](https://codely.com/en) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./elearning.md)*
- **(2024)** [==Tabularis: Open Source Desktop Client for Modern Databases with AI and MCP' Integration==](https://github.com/TabularisDB/tabularis/blob/main/README.es.md) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./databases.md)*
- **(2024)** [==NodeJS Best Practices (Spanish Translation)==](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./golang.md)*
- **(2024)** [==genbeta.com: Hace 20 años, este correo de Jeff Bezos en Amazon cambió para siempre la forma en que programamos apps==](https://www.genbeta.com/desarrollo/hace-22-anos-este-correo-jeff-bezos-amazon-cambio-para-siempre-forma-que-programamos-apps) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./api.md)*
- **(2023)** [==mariocortes.net: La crisis de seniority==](https://www.mariocortes.net/la-crisis-de-seniority) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[ES CONTENT]</span> — *Go to [Section](./project-management-methodology.md)*
- **(2022)** [==estrategiadeproducto.com: La espiral de mierda==](https://www.estrategiadeproducto.com/p/evitar-caer-espiral-de-mierda) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[ES CONTENT]</span> — *Go to [Section](./project-management-methodology.md)*
- **(2026)** [**DevOps Tools**](https://nubenetes.com/devops-tools/) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**NoOps**](https://nubenetes.com/noops/) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**DevOps Tools**](https://nubenetes.com/devops-tools) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**NoOps**](https://nubenetes.com/noops) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops.md)*
- **(2026)** [**BBVA API Market**](https://www.bbvaapimarket.com/es) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./developerportals.md)*
- **(2026)** [**Keptn**](https://nubenetes.com/keptn/) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops-tools.md)*
- **(2026)** [**Keptn**](https://nubenetes.com/keptn) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./devops-tools.md)*
- **(2024)** [**xataka.com: El Excel se ha usado en la Fórmula 1 hasta que se han dado cuenta que no es la mejor forma de controlar las 20.000 piezas del coche**](https://www.xataka.com/automovil/excel-se-ha-usado-formula-1-que-se-han-dado-cuenta-que-no-mejor-forma-controlar-20-000-piezas-coche) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./customer.md)*
- **(2024)** [**redhat.com: OpenShift Backup and Recovery with Kasten K10**](https://www.redhat.com/es/blog) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[SPANISH CONTENT]</span> — *Go to [Section](./ansible.md)*
- **(2023)** [**businessinsider.es: Avanzar en la carrera profesional y conseguir ascensos dentro de la empresa será mucho más difícil para las personas que teletrabajan, según el CEO de IBM**](https://www.businessinsider.es/desarrollo-profesional/teletrabajar-perjudica-carrera-profesional-posibles-ascensos-1240782) 🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[ENTERPRISE-STABLE]</span> <span class='md-tag md-tag--warning'>[ES CONTENT]</span> — *Go to [Section](./project-management-methodology.md)*
@@ -2077,7 +2077,7 @@
<details markdown="1">
<summary>Click to view 1 resources under Go/Typescript Content</summary>
- **(2024)** [Grafana](https://nubenetes.com/grafana/) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[GO/TYPESCRIPT CONTENT]</span> — *Go to [Section](./monitoring.md)*
- **(2024)** [Grafana](https://nubenetes.com/grafana) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[GO/TYPESCRIPT CONTENT]</span> — *Go to [Section](./monitoring.md)*
</details>
@@ -2232,7 +2232,7 @@
- **(2024)** [==aws-samples/aws-network-hub-for-terraform: Network Hub Account with Terraform==](https://github.com/aws-samples/aws-network-hub-for-terraform) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./demos.md)*
- **(2024)** [==registry.terraform.io: Terraform Azure Resources 🌟==](https://registry.terraform.io/modules/azurerm/resources/azure/latest) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./terraform.md)*
- **(2024)** [==Azure-Samples/jmeter-aci-terraform==](https://github.com/Azure-Samples/jmeter-aci-terraform) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./azure.md)*
- **(2024)** [==Terraform Kubernetes Boilerplates 🌟==](https://nubenetes.com/terraform/) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./managed-kubernetes-in-public-cloud.md)*
- **(2024)** [==Terraform Kubernetes Boilerplates 🌟==](https://nubenetes.com/terraform) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./managed-kubernetes-in-public-cloud.md)*
- **(2023)** [==Terraform Automation Demo using Google Cloud Provider==](https://github.com/tfxor/terraform-google-automation-demo) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./demos.md)*
- **(2023)** [==terraform.io: Creation-Time Provisioners 🌟==](https://developer.hashicorp.com/terraform/language/provisioners) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--critical'>[LEGACY]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./terraform.md)*
- **(2023)** [==AWS Lambda the Terraform Way==](https://github.com/nsriram/lambda-the-terraform-way) 🌟🌟🌟🌟🌟 <span class='md-tag md-tag--success'>[DE FACTO STANDARD]</span> <span class='md-tag md-tag--warning'>[HCL CONTENT]</span> — *Go to [Section](./terraform.md)*
@@ -3060,7 +3060,7 @@
- **(2026)** [Librerías cliente](https://prometheus.io/docs/instrumenting/clientlibs) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> — *Go to [Section](./prometheus.md)*
- **(2026)** [refactoring.guru: Design Patterns](https://refactoring.guru/design-patterns) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> — *Go to [Section](./devel-sites.md)*
- **(2024)** [Pulumi Cloud Providers](https://www.pulumi.com/registry/packages) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> — *Go to [Section](./pulumi.md)*
- **(2024)** [AWS Samples (Boilerplates)](https://nubenetes.com/demos/#aws-samples-boilerplates) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> — *Go to [Section](./aws-tools-scripts.md)*
- **(2024)** [AWS Samples (Boilerplates)](https://nubenetes.com/demos) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> — *Go to [Section](./aws-tools-scripts.md)*
- **(2015)** [developers.googleblog.com: Introducing gRPC, a new open source HTTP/2 RPC Framework](https://developers.googleblog.com/introducing-grpc-a-new-open-source-http2-rpc-framework) <span class='md-tag md-tag--info'>[COMMUNITY-TOOL]</span> <span class='md-tag md-tag--warning'>[MULTI-LANGUAGE CONTENT]</span> — *Go to [Section](./api.md)*
</details>
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
# 🎥 Cloud Native Core
# Cloud Native Core
!!! tip "Nubenetes V2 Elite Portal"
You are browsing the AI-Curated V2 Elite Edition. Looking for the exhaustive list of references? Check out the [**V1 Historical Archive**](/v1/).
+1 -1
View File
@@ -1,4 +1,4 @@
# 🎥 DevOps, IaC, and SRE
# DevOps, IaC, and SRE
!!! tip "Nubenetes V2 Elite Portal"
You are browsing the AI-Curated V2 Elite Edition. Looking for the exhaustive list of references? Check out the [**V1 Historical Archive**](/v1/).
+1 -1
View File
@@ -1,4 +1,4 @@
# 🎥 Fundamentals
# Fundamentals
!!! tip "Nubenetes V2 Elite Portal"
You are browsing the AI-Curated V2 Elite Edition. Looking for the exhaustive list of references? Check out the [**V1 Historical Archive**](/v1/).
+49 -15
View File
@@ -12,6 +12,7 @@ edit_uri: "edit/master/v2-docs/"
theme:
name: material
custom_dir: docs/overrides
language: en
favicon: images/favicon-ultra.png
palette:
@@ -35,6 +36,11 @@ theme:
- navigation.sections
- navigation.expand
- navigation.indexes
- navigation.instant
- navigation.instant.prefetch
- navigation.path
- navigation.footer
- announce.dismiss
- search.suggest
- search.highlight
- search.share
@@ -45,7 +51,7 @@ theme:
- navigation.prune
- toc.integrate
plugins:
plugins:
- search
- privacy
- social:
@@ -55,8 +61,29 @@ theme:
font_family: "Inter"
custom_dir: "docs/images/"
logo: favicon-ultra.png
- tags:
tags_file: tags.md
- minify:
minify_html: true
- rss:
match_path: "(tech-digest|industry-digest).*"
date_from_meta:
as_creation: "date"
abstract_chars_count: 200
- redirects:
redirect_maps: {}
redirect_maps:
jvm-parameters-matrix-table.md: java-and-java-performance-optimization.md
private-cloud-solutions.md: kubernetes-on-premise.md
stackstorm.md: cicd.md
chef.md: ansible.md
newsql.md: databases.md
scaleway.md: digitalocean.md
xamarin.md: dotnet.md
dom.md: javascript.md
react.md: javascript.md
oauth.md: securityascode.md
digital-money.md: finops.md
aws-spain.md: aws.md
extra:
social:
@@ -69,10 +96,13 @@ extra:
version:
provider: mike # Ready for version switching
extra_head:
- '<link rel="alternate" type="application/rss+xml" title="Nubenetes Intelligence Digest" href="/feed.xml"/>'
extra_css:
- https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap
- static/extra.css
- static/v2_elite.css?v=2.3.44
- static/v2_elite.css?v=2.7.0
extra_javascript:
- static/v2_filter.js
@@ -95,6 +125,19 @@ markdown_extensions:
- pymdownx.tabbed:
alternate_style: true
- pymdownx.mark
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.keys
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.inlinehilite
- pymdownx.smartsymbols
- pymdownx.caret
- pymdownx.tilde
- tables
- footnotes
- abbr
- def_list
nav:
- "🔙 Back to V1 (Exhaustive)": https://nubenetes.com/v1/
@@ -106,6 +149,9 @@ nav:
- "DevOps, IaC, and SRE": videos/devops-iac.md
- "Cloud Native Core": videos/cloud-native.md
- "Fundamentals": videos/fundamentals.md
- "Intelligence Digest":
- "Tech & Cloud Digest": tech-digest.md
- "Industry & Geo Digest": industry-digest.md
- "AI":
- "AI Agents MCP": ai-agents-mcp.md
- "AI": ai.md
@@ -147,14 +193,12 @@ nav:
- "Testops": testops.md
- "Hardened Infrastructure":
- "Ansible": ansible.md
- "Chef": chef.md
- "Crossplane": crossplane.md
- "Devsecops": devsecops.md
- "IaC": iac.md
- "Kubernetes Security": kubernetes-security.md
- "Kustomize": kustomize.md
- "Liquibase": liquibase.md
- "Oauth": oauth.md
- "Pulumi": pulumi.md
- "Securityascode": securityascode.md
- "Terraform": terraform.md
@@ -175,7 +219,6 @@ nav:
- "AWS Pricing": aws-pricing.md
- "AWS Security": aws-security.md
- "AWS Serverless": aws-serverless.md
- "AWS Spain": aws-spain.md
- "AWS Storage": aws-storage.md
- "AWS Tools Scripts": aws-tools-scripts.md
- "AWS Training": aws-training.md
@@ -186,9 +229,7 @@ nav:
- "Ibm_Cloud": ibm_cloud.md
- "Managed Kubernetes In Public Cloud": managed-kubernetes-in-public-cloud.md
- "Oraclecloud": oraclecloud.md
- "Private Cloud Solutions": private-cloud-solutions.md
- "Public Cloud Solutions": public-cloud-solutions.md
- "Scaleway": scaleway.md
- "Networking & Service Mesh":
- "Caching": caching.md
- "Cloudflare": cloudflare.md
@@ -223,7 +264,6 @@ nav:
- "Crunchydata": crunchydata.md
- "Databases": databases.md
- "Message Queue": message-queue.md
- "Newsql": newsql.md
- "NoSQL": nosql.md
- "Yaml": yaml.md
- "Engineering Pipeline":
@@ -238,14 +278,12 @@ nav:
- "Openshift Pipelines": openshift-pipelines.md
- "Registries": registries.md
- "Sonarqube": sonarqube.md
- "Stackstorm": stackstorm.md
- "Tekton": tekton.md
- "Developer Ecosystem":
- "Chromedevtools": ChromeDevTools.md
- "Angular": angular.md
- "API": api.md
- "Devel Sites": devel-sites.md
- "Dom": dom.md
- "Dotnet": dotnet.md
- "Embedded Servlet Containers": embedded-servlet-containers.md
- "Golang": golang.md
@@ -253,20 +291,16 @@ nav:
- "Java_App_Servers": java_app_servers.md
- "Java_Frameworks": java_frameworks.md
- "Javascript": javascript.md
- "JVM Parameters Matrix Table": jvm-parameters-matrix-table.md
- "Linux Dev Env": linux-dev-env.md
- "Lowcode Nocode": lowcode-nocode.md
- "Maven Gradle": maven-gradle.md
- "Postman": postman.md
- "Python": python.md
- "React": react.md
- "Swagger Code Generator For Rest APIs": swagger-code-generator-for-rest-apis.md
- "Visual Studio": visual-studio.md
- "Web3": web3.md
- "Xamarin": xamarin.md
- "Career & Industry":
- "Appointment Scheduling": appointment-scheduling.md
- "Digital Money": digital-money.md
- "Elearning": elearning.md
- "Finops": finops.md
- "Freelancing": freelancing.md