#!/usr/bin/env python3 """build.py -- the .org XWiki's three pages in the starshipos-f18.com look (the "StarshipOS.com" design canvas: navy #0A1222, accent #F5891F, JetBrains Mono, Exo 2 headings), from one source. site/org/build.py writes Main.WebHome.xwiki, Admin.WebHome.xwiki, Contributors.WebHome.xwiki (each one {{html}} block) site/org/build.py --canvas also writes /project/Org*.dc.html, the same pages as design-canvas artboards (status badges drawn as placeholders: a canvas loads nothing from Gitea) The {{html}} macro needs a page author with script rights. install.sh puts the pages on the wiki. """ import os import sys sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) import colorlock # noqa: E402 the design's colours over the wiki's high-contrast theme GITEA = "https://gitea.starshipos-f18.org/rajames/LithosAnanke" COM = "https://starshipos-f18.com/" ISSUES = GITEA + "/issues?state=open" ACCENT = "#F5891F" TEAL = "#4FD1B0" FONTS = ('\n' '') H = "font-family: 'Exo 2', sans-serif" CARD = "background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 28px" EYEBROW = ("margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; " "color: {accent}") BTN = ("display: inline-block; padding: 14px 24px; background: {accent}; color: #0A1222; " "font-weight: 700; text-decoration: none; border-radius: 4px") BTN2 = ("display: inline-block; padding: 14px 24px; border: 1px solid #34486A; color: #EEF2F7; " "text-decoration: none; border-radius: 4px") WRAP = "max-width: 1120px; margin: 0 auto" WORKFLOWS = { # file -> (branch for the badge, what it is) "acceptance.yml": (None, "Acceptance -- the gate on every PR into master"), "build.yml": ("StarForth-v4.0.0", "Build -- the quick check on StarForth-v4.0.0"), "nightly.yml": ("master", "Nightly -- acceptance, images to Nexus, proofs (03:00 UTC)"), "nightly-docs.yml": ("master", "Nightly documents (04:00 UTC)"), "runner-image.yml": ("master", "Runner image"), "release.yml": (None, "Release -- a v<version> tag"), } def badge(wf, canvas): branch = WORKFLOWS[wf][0] if canvas: return ('[LIVE STATUS]') q = "?branch=" + branch if branch else "" return ('%s status' % (GITEA, wf, q, wf)) def status_rows(wfs, canvas, runs=True): rows = [] for wf in wfs: link = (' runs' % (GITEA, wf) if runs else "") rows.append('
' '%s' '%s%s
' % (WORKFLOWS[wf][1], badge(wf, canvas), link)) return "\n".join(rows) def nav(here, links): items = [] for label, href, key in links: cur = ' aria-current="page"' if key == here else "" style = ("color: #EEF2F7; text-decoration: none; border-bottom: 2px solid {accent}" if key == here else "color: #C8D1DE; text-decoration: none") items.append('%s' % (href, cur, style, label)) items.append('starshipos-f18.com →' % COM) return ('
\n' '\n
' % (WRAP, links[0][1], H, "".join(items))) FOOTER = ('
\n' '
\n' '© 2026 Robert Allan James · A Starship Foundation project (in formation)\n' 'Source' 'starshipos-f18.com\n
\n
' % (WRAP, GITEA, COM)) def hero(eyebrow, title, text, buttons=""): return ('
\n' '

%s

\n' '

%s

\n' '

%s

\n%s' '
' % (WRAP, EYEBROW.replace("{accent}", TEAL), eyebrow, H, title, text, ('
%s
\n' % buttons) if buttons else "")) def section(eyebrow, title, body, pad="48px 24px"): return ('
\n

%s

\n' '

%s

\n' '%s\n
' % (WRAP, pad, EYEBROW, eyebrow, H, title, body)) def card(title, sub, body): return ('
\n

%s

\n' '

%s

\n%s\n
' % (CARD, H, title, sub, body)) def grid(items, min_w=280): return ('
\n%s\n
' % (min_w, "\n".join(items))) def links(items): return ('' % "".join("
  • %s
  • " % i for i in items)) def a(href, text): return '%s' % (href, text) def code(s): return '%s' % s def page_main(canvas, url): links_ = [("Home", url("Main"), "Main"), ("Contributors", url("Contributors"), "Contributors")] glance = [("v2.1.0", "LithosAnanke, the UEFI microkernel"), ("v4", "StarForth, the F18 engine"), ("3 ISAs", "amd64 · aarch64 · riscv64"), ("538", "POST checks on every boot")] strip = ('
    \n
    %s
    \n
    ' % (WRAP, "".join('
    %s
    ' '
    %s
    ' % (H, n, t) for n, t in glance))) project = section("The project", "One repository, one process, one line to master.", grid([ card("The source", "kernel, StarForth, proofs, documents", '

    Everything in one repository: LithosAnanke, ' 'StarForth v3 and v4, the Isabelle/HOL proofs and the books.

    ' + a(GITEA, "Browse it →")), card("Issues", "found, ruled, built, verified", '

    Every piece of work starts as an issue with its ' 'evidence, and is ruled before it is built.

    ' + a(GITEA + "/issues", "Open issues →")), card("Pull requests", "the only way into master", '

    master takes nothing but a pull request that ' 'passed the acceptance protocol.

    ' + a(GITEA + "/pulls", "Pull requests →")), ]), pad="96px 24px 48px") accept = section("How a change is accepted", "Booted on three architectures, every time.", '

    The kernel boots in QEMU ' 'on amd64, aarch64 and riscv64 to its prompt, its power-on self-test passing every check; ' 'the PC stick boots loaded the way a PC loads it; the 32-bit build passes its tests. A ' 'developer runs it by hand, a pull request runs it by itself, and the nightly runs it ' 'again before it publishes.

    \n
    %s
    ' % (CARD, status_rows(["acceptance.yml", "nightly.yml", "nightly-docs.yml"], canvas, False))) contrib = ('
    \n
    \n
    \n' '

    Contributors

    \n

    ' 'Working on StarshipOS?

    \n

    Contributors have a dashboard ' 'of their own: the method, the acceptance commands, the open work. Log in to see it.

    \n
    \n' '
    \n' 'Log in\n' 'Contributors\' dashboard\n
    \n
    \n
    ' % (WRAP, CARD, EYEBROW, H, url("login"), BTN, url("Contributors"), BTN2)) return "\n".join([ nav("Main", links_), hero("ok> the working home", "Where StarshipOS is built.", "StarshipOS is an operating system with no other operating system underneath it. LithosAnanke, a " "UEFI microkernel, boots straight from the firmware and runs StarForth, a FORTH virtual machine, " "as its only runtime. This wiki holds its design, its record and its dashboards; the public face " "is " + a(COM, "starshipos-f18.com") + ".", 'Browse the sourceTry it in a browser' % (GITEA, BTN, COM + "bin/view/TryIt/", BTN2)), strip, project, accept, contrib, FOOTER]) def page_admin(canvas, url): links_ = [("Home", url("Main"), "Main"), ("Contributors", url("Contributors"), "Contributors"), ("Admin", url("Admin"), "Admin")] ci = section("CI", "Every workflow, live.", '
    %s
    ' % (CARD, status_rows(list(WORKFLOWS), canvas)), pad="0 24px 48px") work = card("Work", "Gitea", links([ a(GITEA + "/pulls?state=open", "Open pull requests"), a(ISSUES, "Open issues") + " — " + a(ISSUES + "&labels=23", "in progress") + ", " + a(ISSUES + "&labels=20", "blocked") + ", " + a(ISSUES + "&labels=21", "needs acceptance"), a(GITEA + "/milestones", "Milestones") + " and " + a(GITEA + "/projects", "the project board"), a(GITEA + "/settings/branches", "Branch protection") + " — master: no pushes, merges only after Acceptance", a(GITEA + "/settings/actions/secrets", "Actions secrets") + " — " + code("NEXUS_CI_PASSWORD") + ", " + code("PACKAGES_TOKEN") + ", " + code("CAPSULE_SIGN_KEY"), a(GITEA + "/settings/actions/runners", "Runners"), a("https://gitea.starshipos-f18.org/user/settings/applications", "Access tokens") + " — revoke what a session no longer needs"])) arts = card("Artifacts", "Nexus and packages", links([ a("https://nexus.starshipos-f18.org/#browse/browse:starshipos-snapshots:org%2Fstarship", "Snapshots") + " — " + code("kernelImage") + " (pc, raspi5, milkv, logs), " + code("documentation") + " (book, vol1–vol3, api)", a("https://nexus.starshipos-f18.org/#browse/browse:starshipos-releases:org%2Fstarship", "Releases"), a("https://gitea.starshipos-f18.org/rajames/-/packages", "Container images") + " — " + code("lithosananke-runner"), ])) sites = card("Sites", "and the wiki itself", links([ a(COM, "starshipos-f18.com") + " and " + a(COM + "bin/view/TryIt/", "Try it"), a(url("Contributors"), "Contributors' dashboard"), a(url("admin"), "Wiki administration") + " — users, the group " + code("XWiki.StarshipContributors") + ", rights"])) return "\n".join([ nav("Admin", links_), hero("Administrator only", "Admin dashboard.", "Seen by " + code("XWiki.rajames") + " alone: the Admin space allows view and edit to that " "account and nobody else."), ci, section("Run the project", "Work, artifacts, sites.", grid([work, arts, sites], 300), pad="0 24px 96px"), FOOTER]) def page_contrib(canvas, url): links_ = [("Home", url("Main"), "Main"), ("Contributors", url("Contributors"), "Contributors")] steps = [("Found", "An issue says what is wrong or missing, with the evidence: a log, a line of code."), ("Ruled", "Captain Bob decides what is to be done. Nothing is built without a ruling, and no fix " "nobody asked for."), ("Built", "On the working branch, " + code("StarForth-v4.0.0") + ". C99, no GNU extensions; compose " "in FORTH before adding a C word."), ("Verified", "The acceptance protocol, below; its logs are committed under " + code("logs/") + "."), ("Merged", "A pull request into " + code("master") + ", which runs acceptance by itself. master takes " "nothing else.")] method = section("How we work", "The Captain Bob method.", '

    Neither agile nor waterfall: ' 'one process from the developer\'s desk to master.

    \n' + grid( ['
    ' '%d

    %s

    ' '

    %s

    ' % (CARD, H, i + 1, H, t, d) for i, (t, d) in enumerate(steps)], 190), pad="0 24px 48px") cmds = ("make -f kernel/Makefile ARCH=amd64 clean qemu\n" "make -f kernel/Makefile ARCH=aarch64 clean qemu\n" "make -f kernel/Makefile ARCH=riscv64 clean qemu\n" "make -f kernel/Makefile ARCH=amd64 clean qemu-stick-high\n" "make -C v4 test-32") accept = section("The acceptance protocol", "In this order, one QEMU at a time, in the foreground.", '
    '
                         '%s
    \n

    Each boot reaches ' '%s with %s and every POST check passing, and the three architectures report the same ' '%s. %s installs everything this needs.

    \n
    %s
    ' % (cmds, code("[zuse@Hera] ok>"), code("POST: PASSED"), code("dict_hash"), code("make ARCH=amd64,aarch64,riscv64 init"), CARD, status_rows(["acceptance.yml", "build.yml", "nightly.yml"], canvas))) where = card("Where things are", "start here", links([ a(GITEA, "The repository") + " — " + code("README.md") + ", " + code("docs/README.md") + "; the design record is " + code("docs/fabric/FABRIC-3.md"), a(GITEA + "/issues/new/choose", "New issue") + " — the forms: bug, work, ruling, board", a(ISSUES + "&labels=2", "Open features") + " and " + a(ISSUES + "&labels=1", "bugs"), a(ISSUES + "&milestone=2", "Board bring-up") + " — " + code("boards/BRINGUP.md"), "Before touching a capsule: read " + code("experiments/bare_metal/README.md") + " — the block ranges are shared by every capsule"])) rules = card("Rules that bite", "learned the hard way", links([ "No new branch, no " + code("git stash") + ", without Captain Bob's say-so.", "Never modify a registered, tested FORTH word to fix it: the problem is in the caller.", "ACL policy lives in " + code("ACL.4th") + ", not in C.", "Tags are sacred: each has the logs proving its state."])) return "\n".join([ nav("Contributors", links_), hero("Contributors", "Contributors' dashboard.", "For the members of " + code("XWiki.StarshipContributors") + " and the administrator. " "Ask Captain Bob to be added."), method, accept, section("Reference", "Where things are, and what not to do.", grid([where, rules], 360), pad="0 24px 96px"), FOOTER]) PAGES = { # name -> (builder, title, canvas file, canvas height) "Main": (page_main, "StarshipOS", "OrgHome.dc.html", 2300), "Admin": (page_admin, "Admin Dashboard", "OrgAdmin.dc.html", 1900), "Contributors": (page_contrib, "Contributors' Dashboard", "OrgContributors.dc.html", 2500), } def wiki_url(key): return {"login": "/bin/login/XWiki/XWikiLogin?xredirect=/bin/view/Contributors/", "admin": "/bin/admin/XWiki/XWikiPreferences"}.get(key, "/bin/view/%s/" % key) def canvas_url(key): files = {k: v[2] for k, v in PAGES.items()} return files.get(key, "https://starshipos-f18.org" + wiki_url(key)) def body(name, canvas): html = PAGES[name][0](canvas, canvas_url if canvas else wiki_url) return ('
    \n%s\n
    ' % html) def write_wiki(name): html = colorlock.lock(body(name, False).replace("{accent}", ACCENT), "
    \n\n\n\nStarshipOS.org — %s' '\n\n\n\n\n\n%s\n\n\n%s\n' '\n\n\n\n' % (title, FONTS, html, h)) os.makedirs(os.path.join(root, "project"), exist_ok=True) with open(os.path.join(root, "project", file), "w") as f: f.write(doc) if __name__ == "__main__": os.chdir(os.path.dirname(os.path.abspath(__file__))) canvas_root = sys.argv[2] if len(sys.argv) > 2 and sys.argv[1] == "--canvas" else None for n in PAGES: write_wiki(n) if canvas_root: write_canvas(n, canvas_root) print("wrote", ", ".join("%s.WebHome.xwiki" % n for n in PAGES), "and the canvas artboards" if canvas_root else "")