v0.2.23
RustBox CI / rustbox (push) Canceled after 0s

This commit is contained in:
Mateusz Gruszczyński committed 2026-11-04 20:44:31 +01:00
1 parent 3b9110c8c8
commit beae94e932
42 files changed
+2518 -274

No files matched your search

+72
View File
@@ -941,6 +941,78 @@ try:
except Exception as exc:
errors.append(f"shell syntax validation failed: {exc}")
# Interface-style standard (0.2.23): every predefined style must be listed in
# the Rust catalog, the settings selector, the JS layout profiles, both palettes
# and the structural token table, so a style can never be half-defined.
skin_catalog_match = re.search(r"pub const INTERFACE_SKINS: &\[&str\] = &\[(.*?)\];", display_prefs_rs, re.S)
skin_catalog = re.findall(r'"([a-z0-9-]+)"', skin_catalog_match.group(1)) if skin_catalog_match else []
require(len(skin_catalog) >= 30 and len(skin_catalog) == len(set(skin_catalog)) and "default" not in skin_catalog, "Rust interface-style catalog is complete and duplicate-free")
require("crate::db::display_preferences::INTERFACE_SKINS" in read("src/config.rs"), "server default interface style is validated against the shared Rust catalog")
skin_select = re.search(r'<select id="pref-interface-skin">(.*?)</select>', index_html, re.S)
skin_options = re.findall(r'<option value="([a-z0-9-]+)"', skin_select.group(1)) if skin_select else []
require(skin_options == ["default", *skin_catalog], "settings interface-style selector lists exactly the Rust catalog in order")
prefs_js = read("src/web/js/20-preferences-auth.js")
profile_block = prefs_js.split("const interfaceStyleProfiles = {", 1)[1].split("\n};", 1)[0] if "const interfaceStyleProfiles = {" in prefs_js else ""
profile_keys = re.findall(r'^ "?([a-z0-9-]+)"?: \{$', profile_block, re.M)
require(sorted(profile_keys) == sorted(skin for skin in skin_catalog if skin != "rustbox"), "every predefined style has a fixed JS layout profile")
palette_css = read("src/web/css/55-roundcube-skins.css") + read("src/web/css/56-interface-styles.css")
missing_palettes = [
f"{skin}/{theme}"
for skin in skin_catalog if skin != "rustbox"
for theme in ("light", "dark")
if not re.search(rf'html\[data-interface-skin="{re.escape(skin)}"\]\[data-theme="{theme}"\]\s*\{{[^}}]*--accent:', palette_css)
]
require(not missing_palettes, "every predefined style ships light and dark palettes" + (f" (missing: {', '.join(missing_palettes)})" if missing_palettes else ""))
token_css = read("src/web/css/57-skin-tokens.css")
missing_tokens = [skin for skin in skin_catalog if f'html[data-interface-skin="{skin}"] {{' not in token_css]
require(not missing_tokens, "every interface style declares structural tokens" + (f" (missing: {', '.join(missing_tokens)})" if missing_tokens else ""))
root_tokens = set(re.findall(r"--(skin-[a-z-]+):", token_css.split("}", 1)[0]))
used_tokens = set(re.findall(r"var\(--(skin-[a-z-]+)\)", read("src/web/css/58-skin-immersive.css")))
declared_tokens = set(re.findall(r"--(skin-[a-z-]+):", token_css))
require(used_tokens <= root_tokens and declared_tokens <= root_tokens, "immersive rules only use structural tokens that have :root defaults")
css_manifest = [line.strip() for line in read("src/web/css/manifest.txt").splitlines() if line.strip()]
require(
"57-skin-tokens.css" in css_manifest and "58-skin-immersive.css" in css_manifest
and css_manifest.index("56-interface-styles.css") < css_manifest.index("57-skin-tokens.css") < css_manifest.index("58-skin-immersive.css") < css_manifest.index("60-responsive.css"),
"style tokens and immersive rules load after the palettes and before responsive overrides",
)
require(
'pub const SKIN_DEPTHS: &[&str] = &["colors", "standard", "immersive"];' in display_prefs_rs
and 'skinDepth: "skin_depth"' in prefs_js and 'id="pref-skin-depth"' in index_html
and "root.dataset.skinDepth" in prefs_js and 'savePreference("skinDepth"' in bindings_js,
"style depth is persisted, exposed in Appearance and applied to the document",
)
for _, dialect in MIGRATION_DIALECTS:
require(all(column in read(f"migrations/{dialect}/schema.sql") and column in read(f"src/queries/{dialect}/queries.rs") for column in ("skin_depth", "skin_gradients")), f"{dialect} fresh schema and display query include skin_depth and skin_gradients")
try:
conn = sqlite3.connect(":memory:")
conn.executescript("CREATE TABLE u_prod__display_preferences (id INTEGER PRIMARY KEY, interface_skin TEXT NOT NULL DEFAULT 'rustbox'); INSERT INTO u_prod__display_preferences(id, interface_skin) VALUES(1, 'nord');")
conn.executescript(read("migrations/sqlite/V053__skin_depth_and_gradients.sql").replace("{{p}}", "u_prod__"))
require(conn.execute("SELECT interface_skin, skin_depth, skin_gradients FROM u_prod__display_preferences WHERE id=1").fetchone() == ("nord", "standard", 0), "SQLite V053 adds skin_depth and skin_gradients with standard/off defaults for existing tenants")
conn.close()
except Exception as exc:
errors.append(f"SQLite V053 migration execution failed: {exc}")
require(
"pub skin_gradients: bool" in display_prefs_rs and 'skinGradients: "skin_gradients"' in prefs_js
and 'id="pref-skin-gradients"' in index_html and "root.dataset.skinGradients" in prefs_js
and 'savePreference("skinGradients"' in bindings_js,
"style gradients have their own persisted Appearance switch",
)
# Every gradient in the stylesheets must sit in a rule scoped to the switch.
ungated_gradients = []
for css_name in css_manifest:
css_text = re.sub(r"/\*.*?\*/", "", read(f"src/web/css/{css_name}"), flags=re.S)
for match in re.finditer(r"gradient\(", css_text):
block_start = css_text.rfind("{", 0, match.start())
selector_start = max(css_text.rfind("}", 0, block_start), css_text.rfind(";", 0, block_start))
selector = css_text[selector_start + 1:block_start]
if 'data-skin-gradients="true"' not in selector:
ungated_gradients.append(f"{css_name}: {selector.strip()[:60]}")
require(not ungated_gradients, "every stylesheet gradient is gated by the style-gradients switch" + (f" (ungated: {'; '.join(ungated_gradients[:5])})" if ungated_gradients else ""))
mysql_text_literal_defaults = re.findall(r"^\s*(\w+)\s+TEXT(?:\s+NOT NULL)?\s+DEFAULT\s+'", read("migrations/mysql/schema.sql") + "\n" + "\n".join(read(f"migrations/mysql/{name}") for _, name in MIGRATIONS["mysql"]), re.M)
require(not mysql_text_literal_defaults, "MySQL TEXT columns use expression defaults" + (f" (literal: {', '.join(mysql_text_literal_defaults)})" if mysql_text_literal_defaults else ""))
# TODO/release honesty: live integrations remain explicitly pending.
todo = read("TODO.md")
require("[ ] SILO Compose integration validation" in todo, "TODO keeps live SILO validation pending")