Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion site/site-v6-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -181,4 +193,4 @@
setupMobileNavigation();
setupMobileDownload();
resolveRelease().then(renderRelease).catch(() => renderRelease(null));
})();
})();
22 changes: 21 additions & 1 deletion site/site-v6.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.79 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.1M12.03 7.25C11.88 5.02 13.69 3.18 15.77 3c.29 2.58-2.34 4.5-3.74 4.25"/></svg><span>${isIndonesian ? 'Unduh gratis untuk Mac' : 'Download free for Mac'}</span>`;

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;

Expand All @@ -44,4 +64,4 @@
} else {
window.requestAnimationFrame(() => window.setTimeout(loadCore, 0));
}
})();
})();
49 changes: 45 additions & 4 deletions tools/validate-p0-release-boundary.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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",
Expand Down Expand Up @@ -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.")
Expand All @@ -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())