mirror of
https://github.com/nubenetes/awesome-kubernetes.git
synced 2026-07-12 18:00:37 +00:00
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>
This commit is contained in:
13
.github/workflows/06.deploy_final.yml
vendored
13
.github/workflows/06.deploy_final.yml
vendored
@@ -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)
|
||||
|
||||
@@ -177,14 +177,27 @@ class V2VisionEngine:
|
||||
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()
|
||||
|
||||
@@ -1475,7 +1488,7 @@ 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 = [
|
||||
@@ -1493,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)
|
||||
@@ -1504,11 +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)
|
||||
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__":
|
||||
|
||||
Reference in New Issue
Block a user