fix(icons): commit generated icons; installs and regular builds never render

User installs failed with 'resvg-py is missing' because the web/desktop
source builds rendered icons on whatever python was on PATH. The default
brand outputs are now committed; source_build, apps/desktop build.mjs and
the npm/docusaurus pre-hooks consume them directly. Flavored release
bundles (canary/commit) still render into their own product dir.

icons-freshness-check now regenerates and fails on any byte diff.
This commit is contained in:
ethernet committed 2026-09-24 19:17:34 -04:00
1 parent 27df3b8847
commit 4b7229d612
48 files changed
+56 -93

No files matched your search

+16 -10
View File
@@ -1,13 +1,11 @@
name: Icon Assets Freshness
# Guard for the icon pipeline. The sources of truth are the nous-girl SVGs
# (assets/nous-girl-*.svg) and the per-platform backgrounds
# (assets/backgrounds/*.svg). Generated outputs are NOT committed — every
# consuming pipeline regenerates them via scripts/generate-icons.mjs — so
# this lane regenerates everything from a fresh checkout and runs --check,
# which asserts structural invariants (sizes, transparent squircle corners,
# container frame sets). A source change can never land broken. See
# scripts/generate_icons.py.
# The committed icon outputs must match what scripts/generate_icons.py
# renders from assets/nous-girl-*.svg + assets/backgrounds/*.svg. Regular
# builds and user installs consume the committed files and never render, so
# this lane is what keeps them honest: regenerate, structural --check, then
# fail on any byte difference. Fix a red run with
# `node scripts/generate-icons.mjs` on a Hermes runtime interpreter and commit.
#
# Byte-determinism note: resvg renders the same SVG to identical bytes on any
# host for pure-shape artwork. If the master ever contains <text>, font
@@ -22,7 +20,7 @@ permissions:
jobs:
check:
name: Regenerate icons + structural check
name: Regenerate icons + diff against committed
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
@@ -36,7 +34,15 @@ jobs:
cache-python: true
prune-python-cache: true
- name: Regenerate every target, then verify structure
- name: Regenerate every target, verify structure, diff against committed
env:
HERMES_PAYLOAD_TAG: ''
HERMES_BUILD_COMMIT: ''
run: |
node scripts/generate-icons.mjs
node scripts/generate-icons.mjs --check
if ! git diff --exit-code --stat || [ -n "$(git status --porcelain --untracked-files=all)" ]; then
git status --porcelain --untracked-files=all
echo "::error::Committed icons are stale. Run 'node scripts/generate-icons.mjs' and commit the result."
exit 1
fi
-14
View File
@@ -38,20 +38,6 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# tauri::generate_context! embeds the app icons, which are generated,
# not committed. The generator runs on a Hermes runtime interpreter; []
# exports one as HERMES_PYTHON.
- uses: ./.github/actions/setup-pm
with:
toolchain: all
extras: '[]'
cache-python: true
prune-python-cache: true
- name: Generate icons
working-directory: .
run: node scripts/generate-icons.mjs
# Tauri links against the system webkit2gtk on Linux, so the crate does
# not compile without these even for `cargo test --lib`.
- name: Install Tauri system dependencies
-28
View File
@@ -318,32 +318,4 @@ native/fts5_cjk/*.so
# PM lockfile advisory lock; kept in place so waiting writers share its inode
/pm/.lock.json.lock
# Generated icon assets (scripts/generate_icons.py). Regenerated on demand by
# every consuming pipeline; sources of truth: assets/nous-girl-*.svg + assets/backgrounds/.
assets/icon-master.svg
assets/icon-master-dark.svg
apps/desktop/assets/icon.png
apps/desktop/assets/icon.ico
apps/desktop/assets/icon.icns
apps/desktop/assets/icon-dark.png
apps/desktop/assets/icon-dark.ico
apps/desktop/assets/icon-dark.icns
apps/desktop/assets/appx/*.png
apps/desktop/public/apple-touch-icon.png
apps/desktop/public/nous-girl.png
apps/desktop/public/nous-girl-dark.png
apps/desktop/.dist-build*
apps/bootstrap-installer/src-tauri/icons/*.png
apps/bootstrap-installer/src-tauri/icons/icon.ico
apps/bootstrap-installer/src-tauri/icons/icon.icns
apps/bootstrap-installer/public/nous-girl.png
website/static/img/logo.png
website/static/img/logo-dark.png
website/static/img/nous-logo.png
website/static/img/nous-logo-dark.png
website/static/img/favicon-16x16.png
website/static/img/favicon-32x32.png
website/static/img/apple-touch-icon.png
website/static/img/favicon.ico
website/static/img/favicon.svg
web/public/favicon.ico
+2 -1
View File
@@ -197,7 +197,8 @@ Native desktop dependencies can also require the platform build toolchain.
Logos and icons are generated from `assets/nous-girl-*.svg` and
`assets/backgrounds/`. `node scripts/generate-icons.mjs` renders them with the
Hermes runtime Python (`HERMES_PYTHON`, else `python` on PATH): Pillow and
resvg-py are core dependencies. Do not commit generated PNG/ICO/ICNS outputs.
resvg-py are core dependencies. Generated outputs are committed and CI fails if
they are stale; rerun the generator and commit after changing any source SVG.
### Run tests
-1
View File
@@ -6,7 +6,6 @@
"type": "module",
"scripts": {
"dev": "vite --host 127.0.0.1 --port 5175",
"prebuild": "node ../../scripts/generate-icons.mjs --source ../.. --out ../..",
"build": "tsc -b && vite build",
"preview": "vite preview",
"tauri": "tauri",
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

+1 -1
View File
@@ -19,7 +19,7 @@
"clean:e2e": "tsc --build tsconfig.e2e.json --clean",
"clean:renderer": "tsc --build tsconfig.json --clean ",
"clean:electron": "tsc --build tsconfig.electron.json --clean",
"dev": "node ../../scripts/generate-icons.mjs && concurrently -k \"npm:dev:renderer\" \"npm:dev:electron\"",
"dev": "concurrently -k \"npm:dev:renderer\" \"npm:dev:electron\"",
"dev:fake-boot": "cross-env HERMES_DESKTOP_BOOT_FAKE=1 HERMES_DESKTOP_BOOT_FAKE_STEP_MS=650 npm run dev",
"dev:mock": "node ../../tests-js/scripts/mock-server.ts",
"dev:renderer": "node scripts/assert-root-install.mjs && npm run clean:renderer && vite --host 127.0.0.1 --port 5174",
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+3 -7
View File
@@ -3,19 +3,15 @@ import { execFileSync } from 'node:child_process'
import { cpSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { parseArgs } from 'node:util'
import { generateIcons } from '../../../scripts/generate-icons.mjs'
import { isMain, repoRoot } from '../../../scripts/build/frontend-common.mjs'
export function buildSourceDesktop({ source = repoRoot, icons, run = execFileSync, generate = generateIcons } = {}) {
export function buildSourceDesktop({ source = repoRoot, icons, run = execFileSync } = {}) {
source = resolve(source)
const app = join(source, 'apps/desktop')
const step = (script, args = []) => run(process.execPath, [join(source, script), ...args], { cwd: app, stdio: 'inherit' })
step('apps/desktop/scripts/assert-root-install.mjs')
if (!icons) {
if (generate(['--source', source, '--out', source]) !== 0) throw new Error('Icon preparation failed')
icons = source
}
icons = resolve(icons)
// Default-brand icons are committed; only flavored release builds pass --icons.
icons = resolve(icons ?? source)
if (icons !== source) {
// electron-builder consumes packaging artwork in the workspace. Copy the
// prepared pixels; do not create another Python environment to redraw them.
+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<defs><clipPath id="icon-silhouette"><rect x="0.0" y="0.0" width="1024.0" height="1024.0" rx="245.0" /></clipPath></defs>
<ns0:rect xmlns:ns0="http://www.w3.org/2000/svg" x="20.8766564864" y="20.8766564864" width="982.2466870272" height="982.2466870272" rx="224.1233435136" fill="#0d1117" stroke="#ffffff" stroke-width="41.7533129728" />
<g clip-path="url(#icon-silhouette)"><svg x="72.149433" y="104.703674" width="872.767801" height="872.767801" viewBox="117.8860869140625 96.45225292968749 5026.234069335937 5390.609247070312" preserveAspectRatio="xMidYMax meet" overflow="visible">
<path id="rect179" style="display:inline;opacity:1;fill:#ffffff;stroke-width:50" d="M 2727.8496 100.02344 C 2712.1357 100.02344 2634.4158 101.29576 2609.7832 104.69336 C 2577.9307 108.51567 2546.0792 112.33831 2526.543 114.03711 C 2491.7175 117.01001 2388.0908 133.57372 2360.0605 139.51953 L 2358.7852 139.51953 C 2353.6888 140.79363 2341.3738 143.34222 2332.4551 145.04102 C 2331.181 145.46572 2330.3299 145.46484 2329.4805 145.46484 C 2323.9593 146.73894 2315.8909 148.43879 2309.0957 149.71289 C 2303.5746 150.98699 2298.4786 151.83615 2294.6562 152.68555 C 2285.3128 154.80905 2270.8718 157.78167 2261.9531 159.48047 C 2216.5102 168.39918 2129.4468 189.21045 2107.7871 195.15625 C 2105.2389 196.00566 2102.691 196.85352 2100.9922 196.85352 C 2094.197 197.27822 2086.128 198.97739 2074.2363 202.375 C 2073.3869 202.375 2072.1131 203.22461 2071.2637 203.22461 C 2069.5649 203.64931 2064.043 205.34787 2060.2207 206.19727 C 2056.3984 207.04667 2052.5762 207.89581 2048.7539 209.16992 C 2025.8201 215.11572 2018.1763 217.2402 2004.5859 221.0625 C 2001.1883 221.91191 1997.3662 222.76106 1993.1191 224.03516 L 1992.2695 224.03516 C 1987.1731 225.30926 1982.076 227.00715 1977.4043 228.28125 C 1969.7597 230.40475 1965.5128 231.2552 1962.1152 232.5293 C 1939.606 238.89981 1918.797 245.69478 1891.1914 255.46289 C 1870.8058 262.2581 1858.4896 266.08029 1855.9414 266.92969 C 1854.2426 267.3544 1852.5426 267.77951 1850.8438 268.62891 C 1849.9943 268.62891 1847.8718 269.47764 1846.5977 269.90234 C 1842.7754 271.17644 1839.3779 272.45159 1835.1309 274.15039 C 1832.5827 274.99979 1826.6357 277.12347 1822.3887 278.82227 C 1818.9911 280.52108 1815.17 281.79426 1811.3477 283.06836 C 1790.5373 290.28827 1782.0417 293.68617 1776.9453 296.23438 C 1772.2736 297.93318 1760.3826 302.18011 1755.7109 303.87891 C 1752.3133 305.15301 1734.0515 311.52253 1725.1328 315.76953 C 1717.0635 319.16714 1685.6351 332.75773 1667.373 340.40234 C 1546.758 389.66759 1324.6407 503.48803 1257.1133 550.20508 C 1255.8392 551.05448 1251.591 553.60305 1248.1934 555.72656 C 1240.9735 560.39826 1230.7807 566.76925 1220.5879 573.56445 C 1191.2836 591.82657 1139.0463 624.95314 1112.7148 644.91406 C 1109.3172 647.46227 1100.8237 653.8315 1086.8086 663.59961 C 1067.2724 677.19002 976.38541 745.14305 955.15039 762.98047 C 890.59592 815.64332 847.70129 854.29111 792.49023 910.35156 C 755.11659 948.1499 701.17922 1007.6068 683.3418 1032.6641 C 682.4924 1033.5135 681.64432 1034.7884 680.79492 1036.0625 C 677.39731 1040.3095 671.87658 1047.9539 666.35547 1055.1738 C 665.93077 1055.1738 665.50586 1056.0234 665.50586 1056.0234 C 642.99674 1087.0265 600.52562 1151.5803 577.5918 1192.3516 C 539.36876 1258.6048 466.74537 1405.5521 451.88086 1450.5703 C 451.45616 1451.8444 450.60742 1453.1174 450.60742 1453.9668 C 445.08631 1462.4608 440.83855 1472.654 439.56445 1483.2715 C 438.29035 1486.6691 436.16626 1493.039 433.19336 1500.6836 C 432.76866 1501.533 432.34375 1501.9592 432.34375 1502.8086 C 425.54854 1521.9201 409.83584 1568.2114 397.51953 1609.832 C 392.84783 1622.5731 386.47727 1648.0547 381.38086 1668.0156 C 377.55856 1682.4554 373.31059 1701.1431 369.48828 1718.9805 C 364.81658 1741.4896 363.11806 1750.8335 362.69336 1756.7793 C 358.44636 1769.5204 356.32401 1786.0833 355.47461 1791.1797 C 354.62521 1799.6737 351.22768 1822.607 347.83008 1844.6914 C 342.73367 1882.4897 341.88281 1890.1351 341.88281 1990.7891 C 341.88281 2091.4431 342.3071 2108.0065 352.5 2166.6152 L 352.5 2167.0391 C 361.41871 2216.3043 372.88719 2257.0767 390.72461 2303.3691 L 390.72461 2304.6426 C 391.99871 2307.6155 393.27257 2310.5883 393.69727 2312.2871 C 395.82077 2319.9318 399.21696 2327.1507 403.88867 2336.4941 C 408.13567 2345.4128 413.65837 2357.305 418.33008 2366.6484 C 437.4416 2404.4467 448.05819 2422.2853 466.32031 2450.7402 C 483.30833 2477.0717 521.9561 2519.1169 549.98633 2545.873 C 565.37302 2561.6647 585.3918 2580.9301 594.95312 2587.8438 C 595.78656 2588.5837 596.9367 2589.3677 598.40234 2590.4668 C 602.64935 2593.8644 608.17139 2598.1104 614.11719 2602.3574 C 660.83424 2639.3064 751.29459 2690.696 805.65625 2705.5605 C 806.50565 2705.9852 807.78059 2706.834 809.05469 2706.834 C 813.7264 2708.5328 821.36972 2711.0816 827.74023 2713.2051 C 828.16493 2713.6298 829.01475 2713.6309 829.43945 2713.6309 C 878.28 2729.7695 899.09036 2734.0158 913.10547 2734.8652 C 910.98197 2756.9496 908.0098 2784.9806 904.1875 2821.0801 L 904.1875 2821.9297 C 903.3381 2831.6978 902.48699 2843.1638 901.21289 2853.7812 C 899.51409 2872.0433 897.8162 2889.4567 896.9668 2900.0742 C 896.9668 2904.3212 896.11719 2908.5685 896.11719 2913.2402 C 894.84309 2927.2553 893.5701 2943.3949 892.7207 2952.7383 L 891.87109 2959.5332 C 890.59699 2975.2471 888.04841 3001.1539 886.34961 3023.2383 C 885.92491 3027.4853 885.07617 3031.7306 885.07617 3036.4023 C 883.37737 3056.7879 880.82771 3081.8453 879.12891 3092.4629 L 879.12891 3095.0117 C 877.43011 3110.3009 875.73242 3131.1119 875.73242 3141.3047 C 875.73242 3145.127 874.0Line truncated
</svg></g>
</svg>

After

Width:  |  Height:  |  Size: 135 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<defs><clipPath id="icon-silhouette"><rect x="0.0" y="0.0" width="1024.0" height="1024.0" rx="245.0" /></clipPath></defs>
<ns0:rect xmlns:ns0="http://www.w3.org/2000/svg" x="20.8766564864" y="20.8766564864" width="982.2466870272" height="982.2466870272" rx="224.1233435136" fill="#ffffff" stroke="#000000" stroke-width="41.7533129728" />
<g clip-path="url(#icon-silhouette)"><svg x="72.149433" y="104.703674" width="872.767801" height="872.767801" viewBox="182.1875888671875 150.036837890625 4951.215650390624 5337.024662109375" preserveAspectRatio="xMidYMax meet" overflow="visible">
<path id="path14" style="opacity:1;fill:#000000" d="M 2780.0999 2572.1925 C 2731.3999 2572.7925 2683.4019 2574.5933 2673.3019 2576.2933 C 2641.3019 2580.3933 2609.7995 2583.9939 2590.1995 2585.7939 C 2558.6995 2588.6939 2461.4006 2604.1934 2436.0006 2609.4934 C 2430.6006 2610.6934 2417.6007 2613.5942 2407.5007 2615.3942 C 2396.9007 2617.7942 2381.4 2620.6937 2373.1 2622.4937 C 2364.3 2624.2937 2350.0005 2627.1938 2341.1005 2628.9938 C 2297.8005 2637.7938 2215.4009 2657.4932 2195.8009 2662.7932 C 2189.3009 2664.5932 2181.499 2666.2947 2179.199 2666.2947 C 2176.899 2666.2947 2169.8014 2668.0943 2163.8014 2669.7943 C 2158.5014 2671.5943 2146.5999 2674.4938 2138.1999 2676.8938 C 2110.2999 2683.9938 2106.2018 2685.2929 2084.8018 2691.1929 C 2072.9018 2694.6929 2059.8019 2698.2929 2056.3019 2699.4929 C 2034.9019 2705.3929 2015.3015 2711.9934 1989.8015 2720.8934 C 1973.8015 2726.1934 1957.1997 2731.5932 1952.9997 2732.7932 C 1948.2997 2734.5932 1942.3011 2736.2934 1939.9011 2737.4934 C 1937.0011 2738.6934 1925.1005 2742.8921 1913.8005 2746.9921 C 1901.9005 2751.0921 1888.8006 2755.7914 1885.3006 2757.6914 C 1881.1006 2759.4914 1869.9003 2763.5918 1860.3003 2767.0918 C 1850.3003 2770.5918 1839.0002 2774.8924 1835.4002 2776.5924 C 1827.7002 2780.0924 1796.302 2793.7927 1777.302 2801.5927 C 1661.602 2848.9927 1452.2004 2956.3923 1391.6004 2998.4923 C 1387.4004 3001.3923 1370.9005 3011.4918 1354.8005 3022.1918 C 1312.0005 3048.8918 1272.9013 3074.3922 1253.9013 3088.6922 C 1249.2013 3092.1922 1237.2994 3100.5915 1227.1994 3107.6915 C 1207.5994 3121.3915 1120.4013 3186.4936 1103.8013 3200.7936 C 1042.1013 3251.1936 1002.2989 3287.3931 950.69897 3339.5931 C 911.59897 3379.3931 864.10036 3432.7915 851.00036 3451.1915 C 849.20036 3453.5915 841.50097 3464.2936 833.80097 3474.2936 C 813.60097 3501.5936 773.80044 3562.0926 752.50044 3599.9926 C 713.90044 3666.9926 647.50056 3803.4919 635.60056 3839.0919 C 632.70056 3847.8919 629.00156 3855.6932 627.80156 3856.2932 C 626.60156 3857.4932 625.99975 3859.7927 625.99975 3861.5927 C 625.99975 3863.3927 621.90105 3876.992 616.50105 3890.692 C 609.40105 3910.892 594.60095 3954.7915 583.30095 3992.6915 C 580.90095 3998.5915 572.69995 4030.0928 568.99995 4044.8928 C 561.79995 4073.3928 552.3999 4117.2925 552.3999 4122.0925 C 552.3999 4126.8925 551.20065 4130.4923 550.00065 4133.9923 C 548.80065 4136.8923 546.50045 4147.6925 545.30045 4157.0925 C 544.10045 4166.4925 540.59977 4190.2931 537.59977 4209.8931 C 532.89977 4242.4931 532.30212 4253.6937 532.30212 4342.7937 C 532.30212 4431.8937 532.89873 4451.2934 541.69873 4502.8934 C 549.49873 4546.1934 559.50006 4582.3942 575.50006 4623.8942 C 578.40006 4630.9942 580.80149 4638.1924 580.80149 4638.6924 C 580.80149 4639.8924 589.70013 4658.2925 600.40013 4680.1925 C 617.00013 4713.9925 625.99914 4728.8923 643.19914 4755.5923 C 655.09914 4773.9923 687.60031 4810.7922 715.60031 4837.4922 C 733.40031 4855.2922 750.00089 4870.6928 751.80089 4871.8928 C 753.60089 4873.0928 762.50077 4880.2942 772.00077 4887.2942 C 815.80077 4922.2942 897.69957 4966.7939 938.09957 4977.3939 C 941.59957 4978.5939 952.89964 4982.0941 962.99964 4985.7941 C 1000.3996 4998.2941 1026.501 5005.3935 1031.801 5002.9935 C 1034.201 5002.3935 1033.6004 5001.1932 1030.6004 5000.0932 C 1017.5004 4994.6932 987.80012 4972.1933 970.60012 4954.9933 C 960.60012 4943.6933 945.70125 4925.8918 938.00125 4914.6918 C 923.20125 4892.7918 892.90185 4838.7923 885.80185 4820.9923 C 883.40185 4815.0923 879.3008 4805.5921 877.0008 4799.6921 C 874.6008 4793.7921 871.70069 4786.5945 870.50069 4784.2945 C 867.60069 4778.3945 857.9995 4751.0939 851.4995 4731.4939 C 846.1995 4716.6939 845.59958 4707.0929 849.69958 4707.0929 C 851.49958 4706.4929 857.39972 4717.0931 864.49972 4730.1931 C 892.39972 4784.7931 936.9016 4841.7937 987.9016 4887.9937 C 1020.5016 4917.6937 1072.1013 4958.5931 1078.0013 4958.5931 C 1083.9013 4958.5931 1082.0991 4960.9939 1085.1991 4964.4939 L 1091.6992 4969.7934 L 1089.9011 4999.4939 C 1088.7011 5028.5939 1085.1995 5067.6918 1076.1995 5153.1918 C 1073.7995 5178.7918 1070.3 5212.5939 1069.1 5227.8939 C 1067.9 5243.2939 1066.2009 5266.9933 1064.9009 5280.0933 C 1063.7009 5293.0933 1060.8001 5323.3922 1059.0001 5347.6922 C 1056.6001 5371.9922 1053.7013 5403.9928 1051.3013 5418.8928 C 1049.5013 5433.6928 1048.401 5450.8931 1048.401 5456.7931 C 1048.401 5462.6931 1046.6021 5481.7921 1044.3021 5499.5921 C 1038.4021 5553.5921 1034.1997 5592.0928 1031.1997 5620.5928 C 1027.6997 5658.4928 1022.3018 5708.9931 1019.3018 5728.4931 C 1016.9018 5748.6931 1010.5013 5801.9917 1008.0013 5828.1917 C 1004.5013 5861.9917 998.50199 5911.893 997.30199 5915.393 C 996.10199 5917.193 994.90048 5927.8919 993.80048 5939.6919 C 989.70048 5974.6919 986.70083 5997.1924 982.00083 6031.0924 C 979.10083 6048.8924 976.10138 6070.7934 974.90138 6080.3934 C 973.70138 6090.4934 972.00176 6099.993 971.40176 6102.293 C 970.80176 6104.093 969.00025 6113.5928 967.90025 6122.4928 C 965.50025 6138.4928 963.201Line truncated
</svg></g>
</svg>

After

Width:  |  Height:  |  Size: 114 KiB

+2 -3
View File
@@ -85,9 +85,8 @@ def build_source_tui(project_root: Path, *, env: dict) -> None:
def build_source_web(project_root: Path, *, env: dict, icons: Path | None = None) -> None:
if icons is None:
icons = project_root
run_source_script(project_root, "scripts/generate-icons.mjs", env=env, label="Generating icons")
# Default-brand icons are committed; installs never render them.
icons = icons or project_root
run_source_script(project_root, "scripts/build/web.mjs", "--source", str(project_root),
"--icons", str(icons), "--out", str(project_root / "hermes_cli/web_dist"), env=env,
label="Building the web UI")
+3 -7
View File
@@ -36,13 +36,9 @@ She renders in front of the border, clipped only to the outer rounded silhouette
Only nodes near her bottom edge extend to the border; the fitted face and hair stay fixed.
Standalone wordmarks remain centered and have no border.
GENERATED OUTPUTS ARE NOT COMMITTED. Everything this script writes is
gitignored and regenerated on demand by the consuming pipelines (website
prebuild, desktop prebuild/predev, installer prebuild, web prebuild — via
scripts/generate-icons.mjs). The freshness lane (icons-freshness-check.yml)
runs --check, which regenerates in memory and asserts structural invariants
(sizes, transparency, container frame sets) — there are no committed bytes to
byte-compare against.
GENERATED OUTPUTS ARE COMMITTED. Regular builds and installs consume them and
never render; flavored release bundles (canary/commit) render to a product dir.
icons-freshness-check.yml regenerates, runs --check, and fails on any diff.
Rendering: resvg (resvg-py) for SVG -> PNG fidelity at every size.
Containers: Pillow for multi-size .ico and .icns.
-1
View File
@@ -15,7 +15,6 @@ test('desktop development composition reuses prepared icon pixels instead of pro
put(join(input.icons, 'apps/desktop/assets/icon.ico'), 'prepared packaging icon')
const commands = []
buildSourceDesktop({ source: input.source, icons: input.icons,
generate: () => { throw new Error('prepared icons must not regenerate') },
run: (command, args) => commands.push([command, ...args]),
})
expect(readFileSync(join(input.source, 'apps/desktop/assets/icon.ico'), 'utf8')).toBe('prepared packaging icon')
+7 -4
View File
@@ -167,7 +167,7 @@ def source_products(source_checkout):
encoding="utf-8",
)
for script, step, output in [
("generate-icons.mjs", "icons", "web/public/favicon.ico"),
("generate-icons.mjs", "icons", "never-rendered-at-install"),
("build/tui.mjs", "tui", "ui-tui/dist/entry.js"),
("build/web.mjs", "web", "hermes_cli/web_dist/index.html"),
]:
@@ -188,6 +188,9 @@ def source_products(source_checkout):
"if (!process.argv.includes('--icons')) await import('../generate-icons.mjs');\n",
encoding="utf-8",
)
# Default-brand icons are committed; updates consume them without rendering.
(root / "web/public").mkdir(parents=True, exist_ok=True)
(root / "web/public/favicon.ico").write_bytes(b"committed icon")
return root, acquired
@@ -252,7 +255,7 @@ def test_update_builds_selected_products_after_one_union_preparation(source_prod
app.write_text("previous app")
build_update_products(root, desktop=desktop)
steps = [event["step"] for event in _events(root)]
assert steps == ["deps", "tui", "icons", "web"] + (["desktop"] if desktop else [])
assert steps == ["deps", "tui", "web"] + (["desktop"] if desktop else [])
assert acquired == ["npm"]
assert (root / "ui-tui/dist/entry.js").read_text() == "tui"
assert (root / "hermes_cli/web_dist/index.html").read_text() == "web"
@@ -264,7 +267,7 @@ def test_update_builds_selected_products_after_one_union_preparation(source_prod
@pytest.mark.platforms("linux")
@pytest.mark.parametrize("step", ["tui", "icons", "web", "desktop"])
@pytest.mark.parametrize("step", ["tui", "web", "desktop"])
def test_update_failure_raises_without_retries_or_replacing_live_app(source_products, step):
from hermes_cli.source_build import build_update_products
@@ -277,7 +280,7 @@ def test_update_failure_raises_without_retries_or_replacing_live_app(source_prod
build_update_products(root, desktop=True)
assert app.read_text() == "previous app"
assert not list((root / "apps/desktop").glob(".staging-*"))
order = ["deps", "tui", "icons", "web", "desktop"]
order = ["deps", "tui", "web", "desktop"]
assert [event["step"] for event in _events(root)] == order[:order.index(step) + 1]
assert acquired == ["npm"]
assert not (Path(os.environ["HERMES_HOME"]) / "desktop-build-stamp.json").exists()
-2
View File
@@ -4,9 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"predev": "node ../scripts/generate-icons.mjs --source .. --out ..",
"dev": "vite",
"prebuild": "node ../scripts/generate-icons.mjs --source .. --out ..",
"build": "node ../scripts/build/web.mjs",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

+1 -14
View File
@@ -33,7 +33,6 @@ const repoRoot = resolve(websiteDir, "..");
const extractScript = join(scriptDir, "extract-skills.py");
const llmsScript = join(scriptDir, "generate-llms-txt.py");
const cronBlueprintsScript = join(scriptDir, "extract-automation-blueprints.py");
const iconGenScript = join(repoRoot, "scripts", "generate-icons.mjs");
const pluginsScript = join(scriptDir, "extract-plugins.py");
const pluginStarsScript = join(scriptDir, "fetch-plugin-stars.py");
const outputFile = join(websiteDir, "static", "api", "skills.json");
@@ -125,19 +124,7 @@ async function ensureUnifiedIndex() {
}
}
// 0) Icon assets — the navbar logo, favicons, and apple-touch-icon are
// generated (not committed). This must fail loudly: a docs build without the
// logo ships a broken navbar.
console.log("[prebuild] generating icon assets…");
{
const r = spawnSync("node", [iconGenScript, "--source", repoRoot, "--out", repoRoot], { stdio: "inherit", cwd: repoRoot });
if (r.status !== 0) {
console.error("[prebuild] icon generation failed — it needs a Hermes runtime Python (HERMES_PYTHON); see the output above");
process.exit(1);
}
}
// 0b) Pull unified index if we don't have a fresh one.
// 0) Pull unified index if we don't have a fresh one.
await ensureUnifiedIndex();
// 1) skills.json — required for the Skills Hub page.
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<defs><clipPath id="icon-silhouette"><rect x="0.0" y="0.0" width="1024.0" height="1024.0" rx="245.0" /></clipPath></defs>
<ns0:rect xmlns:ns0="http://www.w3.org/2000/svg" x="20.8766564864" y="20.8766564864" width="982.2466870272" height="982.2466870272" rx="224.1233435136" fill="#ffffff" stroke="#000000" stroke-width="41.7533129728" />
<g clip-path="url(#icon-silhouette)"><svg x="72.149433" y="104.703674" width="872.767801" height="872.767801" viewBox="182.1875888671875 150.036837890625 4951.215650390624 5337.024662109375" preserveAspectRatio="xMidYMax meet" overflow="visible">
<path id="path14" style="opacity:1;fill:#000000" d="M 2780.0999 2572.1925 C 2731.3999 2572.7925 2683.4019 2574.5933 2673.3019 2576.2933 C 2641.3019 2580.3933 2609.7995 2583.9939 2590.1995 2585.7939 C 2558.6995 2588.6939 2461.4006 2604.1934 2436.0006 2609.4934 C 2430.6006 2610.6934 2417.6007 2613.5942 2407.5007 2615.3942 C 2396.9007 2617.7942 2381.4 2620.6937 2373.1 2622.4937 C 2364.3 2624.2937 2350.0005 2627.1938 2341.1005 2628.9938 C 2297.8005 2637.7938 2215.4009 2657.4932 2195.8009 2662.7932 C 2189.3009 2664.5932 2181.499 2666.2947 2179.199 2666.2947 C 2176.899 2666.2947 2169.8014 2668.0943 2163.8014 2669.7943 C 2158.5014 2671.5943 2146.5999 2674.4938 2138.1999 2676.8938 C 2110.2999 2683.9938 2106.2018 2685.2929 2084.8018 2691.1929 C 2072.9018 2694.6929 2059.8019 2698.2929 2056.3019 2699.4929 C 2034.9019 2705.3929 2015.3015 2711.9934 1989.8015 2720.8934 C 1973.8015 2726.1934 1957.1997 2731.5932 1952.9997 2732.7932 C 1948.2997 2734.5932 1942.3011 2736.2934 1939.9011 2737.4934 C 1937.0011 2738.6934 1925.1005 2742.8921 1913.8005 2746.9921 C 1901.9005 2751.0921 1888.8006 2755.7914 1885.3006 2757.6914 C 1881.1006 2759.4914 1869.9003 2763.5918 1860.3003 2767.0918 C 1850.3003 2770.5918 1839.0002 2774.8924 1835.4002 2776.5924 C 1827.7002 2780.0924 1796.302 2793.7927 1777.302 2801.5927 C 1661.602 2848.9927 1452.2004 2956.3923 1391.6004 2998.4923 C 1387.4004 3001.3923 1370.9005 3011.4918 1354.8005 3022.1918 C 1312.0005 3048.8918 1272.9013 3074.3922 1253.9013 3088.6922 C 1249.2013 3092.1922 1237.2994 3100.5915 1227.1994 3107.6915 C 1207.5994 3121.3915 1120.4013 3186.4936 1103.8013 3200.7936 C 1042.1013 3251.1936 1002.2989 3287.3931 950.69897 3339.5931 C 911.59897 3379.3931 864.10036 3432.7915 851.00036 3451.1915 C 849.20036 3453.5915 841.50097 3464.2936 833.80097 3474.2936 C 813.60097 3501.5936 773.80044 3562.0926 752.50044 3599.9926 C 713.90044 3666.9926 647.50056 3803.4919 635.60056 3839.0919 C 632.70056 3847.8919 629.00156 3855.6932 627.80156 3856.2932 C 626.60156 3857.4932 625.99975 3859.7927 625.99975 3861.5927 C 625.99975 3863.3927 621.90105 3876.992 616.50105 3890.692 C 609.40105 3910.892 594.60095 3954.7915 583.30095 3992.6915 C 580.90095 3998.5915 572.69995 4030.0928 568.99995 4044.8928 C 561.79995 4073.3928 552.3999 4117.2925 552.3999 4122.0925 C 552.3999 4126.8925 551.20065 4130.4923 550.00065 4133.9923 C 548.80065 4136.8923 546.50045 4147.6925 545.30045 4157.0925 C 544.10045 4166.4925 540.59977 4190.2931 537.59977 4209.8931 C 532.89977 4242.4931 532.30212 4253.6937 532.30212 4342.7937 C 532.30212 4431.8937 532.89873 4451.2934 541.69873 4502.8934 C 549.49873 4546.1934 559.50006 4582.3942 575.50006 4623.8942 C 578.40006 4630.9942 580.80149 4638.1924 580.80149 4638.6924 C 580.80149 4639.8924 589.70013 4658.2925 600.40013 4680.1925 C 617.00013 4713.9925 625.99914 4728.8923 643.19914 4755.5923 C 655.09914 4773.9923 687.60031 4810.7922 715.60031 4837.4922 C 733.40031 4855.2922 750.00089 4870.6928 751.80089 4871.8928 C 753.60089 4873.0928 762.50077 4880.2942 772.00077 4887.2942 C 815.80077 4922.2942 897.69957 4966.7939 938.09957 4977.3939 C 941.59957 4978.5939 952.89964 4982.0941 962.99964 4985.7941 C 1000.3996 4998.2941 1026.501 5005.3935 1031.801 5002.9935 C 1034.201 5002.3935 1033.6004 5001.1932 1030.6004 5000.0932 C 1017.5004 4994.6932 987.80012 4972.1933 970.60012 4954.9933 C 960.60012 4943.6933 945.70125 4925.8918 938.00125 4914.6918 C 923.20125 4892.7918 892.90185 4838.7923 885.80185 4820.9923 C 883.40185 4815.0923 879.3008 4805.5921 877.0008 4799.6921 C 874.6008 4793.7921 871.70069 4786.5945 870.50069 4784.2945 C 867.60069 4778.3945 857.9995 4751.0939 851.4995 4731.4939 C 846.1995 4716.6939 845.59958 4707.0929 849.69958 4707.0929 C 851.49958 4706.4929 857.39972 4717.0931 864.49972 4730.1931 C 892.39972 4784.7931 936.9016 4841.7937 987.9016 4887.9937 C 1020.5016 4917.6937 1072.1013 4958.5931 1078.0013 4958.5931 C 1083.9013 4958.5931 1082.0991 4960.9939 1085.1991 4964.4939 L 1091.6992 4969.7934 L 1089.9011 4999.4939 C 1088.7011 5028.5939 1085.1995 5067.6918 1076.1995 5153.1918 C 1073.7995 5178.7918 1070.3 5212.5939 1069.1 5227.8939 C 1067.9 5243.2939 1066.2009 5266.9933 1064.9009 5280.0933 C 1063.7009 5293.0933 1060.8001 5323.3922 1059.0001 5347.6922 C 1056.6001 5371.9922 1053.7013 5403.9928 1051.3013 5418.8928 C 1049.5013 5433.6928 1048.401 5450.8931 1048.401 5456.7931 C 1048.401 5462.6931 1046.6021 5481.7921 1044.3021 5499.5921 C 1038.4021 5553.5921 1034.1997 5592.0928 1031.1997 5620.5928 C 1027.6997 5658.4928 1022.3018 5708.9931 1019.3018 5728.4931 C 1016.9018 5748.6931 1010.5013 5801.9917 1008.0013 5828.1917 C 1004.5013 5861.9917 998.50199 5911.893 997.30199 5915.393 C 996.10199 5917.193 994.90048 5927.8919 993.80048 5939.6919 C 989.70048 5974.6919 986.70083 5997.1924 982.00083 6031.0924 C 979.10083 6048.8924 976.10138 6070.7934 974.90138 6080.3934 C 973.70138 6090.4934 972.00176 6099.993 971.40176 6102.293 C 970.80176 6104.093 969.00025 6113.5928 967.90025 6122.4928 C 965.50025 6138.4928 963.201Line truncated
</svg></g>
</svg>

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB