mirror of
https://github.com/nubenetes/awesome-kubernetes.git
synced 2026-07-12 18:00:37 +00:00
last_checked stored epoch seconds as REAL; SQLite's version-specific REAL->text float printing rewrote ~every row on dump, so a locally regenerated inventory.sql diverged from CI's. Normalize last_checked to int on the entry before both the SQL record and the YAML dump, and make the column INTEGER. All readers do coarse day-level staleness checks, so sub-second precision is unused. Verified: load->save idempotent for inventory.sql AND inventory.yaml, and local (SQLite 3.40.1) output byte-identical to CI-equivalent python:3.11 (SQLite 3.46.1). The deterministic data regenerates on the next pipeline save; committing only the code avoids conflicting with pipeline data churn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
208 lines
7.4 KiB
Python
208 lines
7.4 KiB
Python
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.
|
|
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 not os.path.exists(SQL_PATH) and 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:
|
|
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.
|
|
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 INTEGER,
|
|
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
|
|
|
|
# last_checked is a coarse staleness timestamp (epoch seconds). Normalize
|
|
# it to int on the entry itself — before building the SQL record AND the
|
|
# YAML dump below — so both serializations agree, and SQLite's
|
|
# version-specific REAL->text float formatting can't rewrite every row on
|
|
# dump. Sub-second precision is not used by any reader.
|
|
lc = entry.get("last_checked")
|
|
if isinstance(lc, float):
|
|
entry["last_checked"] = int(lc)
|
|
|
|
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", encoding="utf-8") as file:
|
|
yaml.dump(inv, file, Dumper=Dumper, sort_keys=False, allow_unicode=True)
|
|
|
|
def get_shard_name(url: str) -> str:
|
|
return "inventory.yaml"
|
|
|
|
def update_inventory_entry(inventory: Dict, norm_url: str, new_data: Dict):
|
|
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
|