diff --git a/site/site-v6-core.js b/site/site-v6-core.js
index 5d9511e..c8fd088 100644
--- a/site/site-v6-core.js
+++ b/site/site-v6-core.js
@@ -105,6 +105,18 @@
setLink(document.getElementById('mac-dmg-link'), state.macDmgUrl, Boolean(state.macDmgUrl));
setLink(document.getElementById('mac-vst3-link'), state.macVst3Url, Boolean(state.macVst3Url));
setLink(document.getElementById('mac-standalone-link'), state.macStandaloneUrl, Boolean(state.macStandaloneUrl));
+
+ const heroMac = document.getElementById('mac-dmg-link-hero');
+ if (heroMac && state.macDmgUrl) {
+ setLink(heroMac, state.macDmgUrl, true);
+ } else if (heroMac) {
+ // Never regress to a missing Mac action: retain a useful static fallback
+ // even when release metadata is temporarily unavailable.
+ heroMac.href = '#download';
+ heroMac.removeAttribute('aria-disabled');
+ heroMac.removeAttribute('data-release-pending');
+ }
+
setLink(document.getElementById('checksums-link'), state.checksumsUrl, Boolean(state.checksumsUrl));
setLink(document.getElementById('release-link'), state.releaseUrl || RELEASE_FALLBACK, true);
setLink(document.getElementById('distribution-link'), state.releaseUrl || RELEASE_FALLBACK, true);
@@ -181,4 +193,4 @@
setupMobileNavigation();
setupMobileDownload();
resolveRelease().then(renderRelease).catch(() => renderRelease(null));
-})();
+})();
\ No newline at end of file
diff --git a/site/site-v6.js b/site/site-v6.js
index ad55bdf..8b17ddc 100644
--- a/site/site-v6.js
+++ b/site/site-v6.js
@@ -23,6 +23,26 @@
});
}
+ // Keep both primary platform CTAs visible immediately. The Mac CTA uses a
+ // safe #download fallback first; the idle release runtime upgrades it to the
+ // exact reviewed DMG URL from release.json when that manifest is available.
+ const ensureHeroMacCta = () => {
+ if (document.getElementById('mac-dmg-link-hero')) return;
+ const actions = document.querySelector('.landing-hero .hero-copy .actions');
+ if (!actions) return;
+
+ const link = document.createElement('a');
+ link.id = 'mac-dmg-link-hero';
+ link.className = 'button secondary hero-mac-download';
+ link.href = '#download';
+ link.innerHTML = `${isIndonesian ? 'Unduh gratis untuk Mac' : 'Download free for Mac'}`;
+
+ const explore = actions.querySelector('a[href^="#"]');
+ actions.insertBefore(link, explore || null);
+ };
+
+ ensureHeroMacCta();
+
const source = currentScript?.src
|| new URL(`${root.dataset.siteBase || '.'}/site-v6.js`, location.href).href;
@@ -44,4 +64,4 @@
} else {
window.requestAnimationFrame(() => window.setTimeout(loadCore, 0));
}
-})();
+})();
\ No newline at end of file
diff --git a/tools/validate-p0-release-boundary.py b/tools/validate-p0-release-boundary.py
index 818e4c0..62fff0d 100644
--- a/tools/validate-p0-release-boundary.py
+++ b/tools/validate-p0-release-boundary.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Validate the public release and GitHub Pages security boundary."""
+"""Validate the public release, Pages security boundary and platform CTA contract."""
from __future__ import annotations
@@ -11,6 +11,12 @@
RELEASE_WORKFLOW = WORKFLOWS / "build-macos-and-publish.yml"
PAGES_WORKFLOW = WORKFLOWS / "pages.yml"
UPDATER = ROOT / "tools" / "update-public-crossplatform-release.py"
+LANDING_EN = ROOT / "site" / "index.html"
+LANDING_ID = ROOT / "site" / "id" / "index.html"
+SITE_LOADER = ROOT / "site" / "site-v6.js"
+SITE_CORE = ROOT / "site" / "site-v6-core.js"
+HARDENING_CSS = ROOT / "site" / "hardening-v6.css"
+RELEASE_MANIFEST = ROOT / "site" / "release.json"
APPROVED_WORKFLOWS = [
".github/workflows/build-macos-and-publish.yml",
@@ -222,6 +228,40 @@ def validate_pages_workflow(text: str) -> None:
)
+def validate_platform_cta_contract() -> None:
+ for path in (LANDING_EN, LANDING_ID, SITE_LOADER, SITE_CORE, HARDENING_CSS, RELEASE_MANIFEST):
+ require(path.is_file(), f"Platform CTA contract file is missing: {path.relative_to(ROOT)}")
+
+ landing_en = LANDING_EN.read_text(encoding="utf-8")
+ landing_id = LANDING_ID.read_text(encoding="utf-8")
+ loader = SITE_LOADER.read_text(encoding="utf-8")
+ core = SITE_CORE.read_text(encoding="utf-8")
+ css = HARDENING_CSS.read_text(encoding="utf-8")
+ manifest = RELEASE_MANIFEST.read_text(encoding="utf-8")
+
+ require(
+ 'id="installer-link-bottom"' in landing_en and "Download free for Windows" in landing_en,
+ "English Windows hero CTA is missing.",
+ )
+ require(
+ 'id="installer-link-bottom"' in landing_id and "Unduh gratis untuk Windows" in landing_id,
+ "Indonesian Windows hero CTA is missing.",
+ )
+ require("ensureHeroMacCta" in loader, "Mac hero CTA bootstrap is missing from the lightweight loader.")
+ require("mac-dmg-link-hero" in loader, "Mac hero CTA stable id is missing from the lightweight loader.")
+ require("Download free for Mac" in loader, "English Mac hero CTA label is missing.")
+ require("Unduh gratis untuk Mac" in loader, "Indonesian Mac hero CTA label is missing.")
+ require("link.hidden" not in loader, "Mac hero CTA must not be hidden by the bootstrap loader.")
+ require(
+ "document.getElementById('mac-dmg-link-hero')" in core
+ and "setLink(heroMac, state.macDmgUrl, true)" in core,
+ "Release runtime does not upgrade the Mac hero CTA to the reviewed DMG URL.",
+ )
+ require("heroMac.href = '#download'" in core, "Mac hero CTA has no resilient download-section fallback.")
+ require(".button.secondary.hero-mac-download" in css, "Mac hero CTA visual treatment is missing.")
+ require('"macos-universal"' in manifest and '"macDmgUrl"' in manifest, "macOS release manifest contract is missing.")
+
+
def main() -> int:
require(RELEASE_WORKFLOW.is_file(), "Approved public release workflow is missing.")
require(PAGES_WORKFLOW.is_file(), "Approved Pages workflow is missing.")
@@ -230,13 +270,14 @@ def main() -> int:
validate_workflow_inventory()
validate_release_workflow(RELEASE_WORKFLOW.read_text(encoding="utf-8"))
validate_pages_workflow(PAGES_WORKFLOW.read_text(encoding="utf-8"))
+ validate_platform_cta_contract()
print(
- "[PASS] Public build is read-only; publish is source-free; "
- "Pages is exact-source validated; workflow inventory is allowlisted."
+ "[PASS] Public build is read-only; publish is source-free; Pages is exact-source validated; "
+ "workflow inventory is allowlisted; Windows and Mac hero CTAs are regression-guarded."
)
return 0
if __name__ == "__main__":
- raise SystemExit(main())
+ raise SystemExit(main())
\ No newline at end of file