Release machinery for Downloads, the root CA, demo and site updates (#126) #154
@@ -6,6 +6,7 @@
|
||||
#
|
||||
# org.starship:kernelImage:<LITHOS_VERSION>-SNAPSHOT, starshipos-snapshots
|
||||
# classifier pc StarshipOS.iso amd64, UEFI -- the ISO the amd64 acceptance boot booted
|
||||
# classifier pc-stick pc-stick.img.xz a PC's USB stick, for any UEFI PC (make boot_image TARGET=PC)
|
||||
# classifier raspi5 raspi5.img.xz Raspberry Pi 5, for Raspberry Pi Imager
|
||||
# classifier milkv MilkV.img.xz Milk-V Mars
|
||||
# classifier logs logs.tar.gz this run's serial logs and DoE CSVs (the proof log is in
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
# acceptance protocol, the proofs, the board images -- and the documents, and publishes both to
|
||||
# starshipos-releases at that version:
|
||||
#
|
||||
# org.starship:kernelImage:<version> classifiers pc, raspi5, milkv, logs (nightly.yml)
|
||||
# org.starship:kernelImage:<version> classifiers pc, pc-stick, raspi5, milkv, logs (nightly.yml)
|
||||
# org.starship:documentation:<version> classifiers book, vol1, vol2, vol3, api (nightly-docs.yml)
|
||||
#
|
||||
# and makes the tag a Gitea release -- a pre-release for v<x.y.z>-alpha.N and the like -- with the
|
||||
# images, SHA256SUMS, the documents and the StarshipOS Root CA's certificate attached, for anyone
|
||||
# to download (#126; scripts/nightly.sh gitea-release). Needs the secret RELEASE_TOKEN: a token
|
||||
# that may write the repository.
|
||||
#
|
||||
# The tag must name the version the tree says it is: v<LITHOS_VERSION> (kernel/Makefile). A tag
|
||||
# that does not is refused before anything is built. A release repository takes a version once.
|
||||
name: Release
|
||||
@@ -84,6 +89,11 @@ jobs:
|
||||
scripts/nightly.sh publish https://nexus.starshipos-f18.org/repository/starshipos-releases
|
||||
scripts/nightly.sh publish-docs https://nexus.starshipos-f18.org/repository/starshipos-releases
|
||||
|
||||
- name: Gitea release # the downloads anyone can fetch (#126): no Nexus login
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} # a token that may write the repository
|
||||
run: scripts/nightly.sh gitea-release "${{ github.ref_name }}"
|
||||
|
||||
- name: Keep the release
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
+4
-1
@@ -3,7 +3,10 @@
|
||||
# 2026-11-07: "New root CA on the CI box. Valid StarshipOS rather than snake oil intermediate").
|
||||
#
|
||||
# StarshipOS Root CA Ed25519, 20 years, self-signed. Its key never leaves this box:
|
||||
# not in the repository, not in Gitea, not in any image.
|
||||
# not in the repository, not in Gitea, not in any image. Its
|
||||
# certificate (public) is ci/pki/starshipos-root-ca.pem, made on
|
||||
# 2026-11-07, SHA-256 in starshipos-root-ca.sha256; every release
|
||||
# attaches it.
|
||||
# StarshipOS Capsule Signing Ed25519, 10 years, signed by the root; CA:TRUE, pathlen:0.
|
||||
# Its certificate is built into every image (capsules/pki/
|
||||
# starshipos-intermediate.der) and checks each capsule at boot;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBaDCCARqgAwIBAgIUCwt1JkPxCfwr9PTCOadol/m9PCQwBQYDK2VwMDIxEzAR
|
||||
BgNVBAoMClN0YXJzaGlwT1MxGzAZBgNVBAMMElN0YXJzaGlwT1MgUm9vdCBDQTAe
|
||||
Fw0yNjExMDcxNjUwNTZaFw00NjExMDcxNjUwNTZaMDIxEzARBgNVBAoMClN0YXJz
|
||||
aGlwT1MxGzAZBgNVBAMMElN0YXJzaGlwT1MgUm9vdCBDQTAqMAUGAytlcAMhANcn
|
||||
fPpowbNgzMPtDEA7EYJoSkBLPljM6dEBFONfrhHso0IwQDAPBgNVHRMBAf8EBTAD
|
||||
AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU5eU4Iy/Al5g9KZI5+u4jKllG
|
||||
shswBQYDK2VwA0EAjRK1ybZE/VaH31dt3sc8DgqJVTPB6CYovdqWKcJMXC8UKfYJ
|
||||
co9o8MhiIauvSCgZZA2+5VMv4zlke7Lu5TNLDg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1 @@
|
||||
DA:A7:40:56:65:41:1A:FE:E6:08:85:3F:6E:AD:83:3A:38:30:83:E6:F0:6D:69:A6:A8:9E:92:BD:85:93:39:81
|
||||
@@ -30,13 +30,14 @@ ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ReadOnlyPaths=/var/lib/starshipos-demo
|
||||
# Sized for this server (3.8 GiB, both wikis, Gitea, Nexus, CI): an idle StarshipOS guest keeps a
|
||||
# host core busy -- the kernel's idle loop polls (measured 2026-11-08: 100% of a core at the
|
||||
# prompt, 148 MiB resident) -- so all sessions together get one core at the lowest priority, and
|
||||
# the wikis and Gitea never starve; with two visitors each runs at half speed. 1 GiB holds two
|
||||
# sessions with room. Raise both with the server (8 GB: MemoryMax=2G, CPUQuota=200%).
|
||||
# Sized for this server (3.8 GiB, two cores; both wikis, Gitea, Nexus, CI). Since #151 an idle guest
|
||||
# halts (1% of a core at the prompt, was 100%), so a session costs CPU only while its visitor runs
|
||||
# something. All sessions together get 1.5 cores at the lowest priority (Captain Bob, 2026-11-08):
|
||||
# one busy visitor a full core, two busy at once three-quarters each, and half a core always left
|
||||
# to the wikis and Gitea. 1 GiB holds two sessions (~150 MB resident each) with room. Raise both
|
||||
# with the server (8 GB: MemoryMax=2G, CPUQuota=200% or more).
|
||||
MemoryMax=1G
|
||||
CPUQuota=100%
|
||||
CPUQuota=150%
|
||||
Nice=19
|
||||
TasksMax=200
|
||||
|
||||
|
||||
@@ -6279,3 +6279,27 @@ protocol passes -- amd64 `logs/20261108-005539`, aarch64 `-005753`, riscv64 `-01
|
||||
538/538, `dict_hash 0xfec8c47cfc6f437f` on all three, `7 ok` typed and answered; the stick loaded
|
||||
high; `test-32` (39 programs, 0 failures). The demo's CPU cap (`CPUQuota=100%`) can be relaxed
|
||||
once this reaches the server.
|
||||
|
||||
### 6. #131 closed: QEMU's capture, not our kernel (2026-11-08)
|
||||
|
||||
Run 197's riscv64 serial log, read from Gitea's artifact store on the server (`acceptance-logs-185`),
|
||||
had the line with one character doubled -- "Boot diisk: not a StarshipOS stick" -- and a doubled
|
||||
byte in OpenSBI's banner too, printed before our kernel runs. So the byte was doubled in QEMU's
|
||||
serial capture under the server's load, not by the kernel (Captain Bob closed #131). The check that
|
||||
failed was right to: it refuses a line that is not what was printed. The capture itself (the log is
|
||||
`logfile=` on the socket chardev the acceptance types through) is what is left to harden.
|
||||
|
||||
### 7. Downloads (#126): what a release publishes, and what a download must boot to (2026-11-08)
|
||||
|
||||
- **Ruled** (Captain Bob): the Downloads page goes live with alpha.4, filled by the release itself;
|
||||
the StarshipOS Root CA's certificate is published with it.
|
||||
- **Built:** the release builds the PC's USB stick too (`scripts/nightly.sh images`, classifier
|
||||
`pc-stick`; `pc` stays the ISO QEMU boots), and makes the tag a Gitea release -- a pre-release for
|
||||
an alpha -- with the images, `SHA256SUMS`, the documents and the Root CA's certificate attached
|
||||
for anyone to download (`scripts/nightly.sh gitea-release`, run by `release.yml` with the secret
|
||||
`RELEASE_TOKEN`). Tested against a stand-in Gitea API: the release, its notes and ten files; a
|
||||
second run adds nothing.
|
||||
- **Found:** a downloaded image boots headless today -- "Zuse: no genesis on this disk -- the system
|
||||
stays headless"; minting Zuse on a stick's first boot (`docs/v4.0.0/STICK.md` section 6) is #92.
|
||||
**Ruled:** #92 into alpha.4, so the PC stick downloaded from alpha.4 on boots to
|
||||
`[zuse@Hera] ok>` at its first boot.
|
||||
|
||||
+46
-7
@@ -22,6 +22,7 @@
|
||||
# logs/<TS>/isabelle/, as a boot's is.
|
||||
# scripts/nightly.sh images
|
||||
# The board images, into build/nightly/:
|
||||
# pc-stick.img.xz a PC's USB stick (make boot_image TARGET=PC): GPT, one ESP, any UEFI PC
|
||||
# raspi5.img.xz Raspberry Pi 5 (make boot_image TARGET=RASPI), for Raspberry Pi Imager
|
||||
# MilkV.img.xz Milk-V Mars (make boot_image TARGET=MILKV)
|
||||
# SHA256SUMS of StarshipOS.iso and both images, before and after compression
|
||||
@@ -29,7 +30,7 @@
|
||||
# same sources, for hardware QEMU does not model.
|
||||
# scripts/nightly.sh publish <URL>
|
||||
# org.starship:kernelImage:<VERSION> to that Maven repository, in one deploy (one
|
||||
# timestamp): classifier pc (iso), raspi5 and milkv (img.xz), and logs (tar.gz: this
|
||||
# timestamp): classifier pc (iso, for QEMU), pc-stick, raspi5 and milkv (img.xz), and logs (tar.gz: this
|
||||
# run's serial logs and DoE CSVs -- the evidence for the rest; and the proof log, when the
|
||||
# proofs ran before it, as a release runs them).
|
||||
#
|
||||
@@ -54,7 +55,7 @@ LITHOS=$(sed -n 's/^LITHOS_VERSION *?= *//p' kernel/Makefile)
|
||||
VERSION=${NIGHTLY_VERSION:-$LITHOS-SNAPSHOT}
|
||||
usage() {
|
||||
echo "Usage: scripts/nightly.sh accept <amd64|aarch64|riscv64|stick-high> | test-32 | proofs | images" >&2
|
||||
echo " | publish <URL> | docs | publish-docs <URL>" >&2
|
||||
echo " | publish <URL> | docs | publish-docs <URL> | gitea-release <tag>" >&2
|
||||
exit 2
|
||||
}
|
||||
need() { for f in "$@"; do [ -f "$f" ] || { echo "nightly: no $f -- run the stages before this one" >&2; exit 1; }; done; }
|
||||
@@ -138,7 +139,7 @@ proofs)
|
||||
;;
|
||||
images)
|
||||
need "$OUT/StarshipOS.iso"
|
||||
for b in "RASPI raspi/lithos-raspi.img raspi5.img" "MILKV milkv/lithos-milkv.img MilkV.img"; do
|
||||
for b in "PC pc/lithos-pc.img pc-stick.img" "RASPI raspi/lithos-raspi.img raspi5.img" "MILKV milkv/lithos-milkv.img MilkV.img"; do
|
||||
set -- $b
|
||||
echo "=== nightly: image $1"
|
||||
make --no-print-directory boot_image TARGET="$1"
|
||||
@@ -147,18 +148,20 @@ images)
|
||||
# boot_image regenerates these (the source SBOM, and what qemu_session.py also puts back);
|
||||
# the nightly does not change the tree
|
||||
git checkout -q -- sbom.spdx sbom.spdx.json capsules/BLOCK_MAP.md v3/include/version.h
|
||||
(cd "$OUT" && rm -f raspi5.img.xz MilkV.img.xz && sha256sum StarshipOS.iso raspi5.img MilkV.img > SHA256SUMS)
|
||||
xz -T0 -6 "$OUT/raspi5.img" "$OUT/MilkV.img"
|
||||
(cd "$OUT" && sha256sum raspi5.img.xz MilkV.img.xz >> SHA256SUMS && cat SHA256SUMS)
|
||||
(cd "$OUT" && rm -f pc-stick.img.xz raspi5.img.xz MilkV.img.xz &&
|
||||
sha256sum StarshipOS.iso pc-stick.img raspi5.img MilkV.img > SHA256SUMS)
|
||||
xz -T0 -6 "$OUT/pc-stick.img" "$OUT/raspi5.img" "$OUT/MilkV.img"
|
||||
(cd "$OUT" && sha256sum pc-stick.img.xz raspi5.img.xz MilkV.img.xz >> SHA256SUMS && cat SHA256SUMS)
|
||||
;;
|
||||
publish)
|
||||
url=${2:-}; [ -n "$url" ] || usage
|
||||
need "$OUT/StarshipOS.iso" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz"
|
||||
need "$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz"
|
||||
# the evidence: what this run wrote under logs/ and experiments/bare_metal/runs/
|
||||
find logs experiments/bare_metal/runs -type f -newer "$OUT/.started" -print0 \
|
||||
| tar --null -czf "$OUT/logs.tar.gz" -T -
|
||||
echo "=== nightly: logs.tar.gz: $(tar -tzf "$OUT/logs.tar.gz" | wc -l) files"
|
||||
deploy "$url" kernelImage "$OUT/StarshipOS.iso" iso pc \
|
||||
"$OUT/pc-stick.img.xz" img.xz pc-stick \
|
||||
"$OUT/raspi5.img.xz" img.xz raspi5 \
|
||||
"$OUT/MilkV.img.xz" img.xz milkv \
|
||||
"$OUT/logs.tar.gz" tar.gz logs
|
||||
@@ -172,6 +175,42 @@ docs)
|
||||
cp docs/formal/build/doxygen/StarForth-API-Reference.pdf "$DOCS/api.pdf"
|
||||
(cd "$DOCS" && sha256sum ./*.pdf | tee SHA256SUMS)
|
||||
;;
|
||||
gitea-release)
|
||||
# The release as Gitea's own (#126): a release for <tag> -- a pre-release when the tag names
|
||||
# one (v4.0.0-alpha.3) -- its notes the tag's message and the checksums, its files everyone's
|
||||
# to download from the public repository, no Nexus login: the images, SHA256SUMS, the
|
||||
# documents, and the StarshipOS Root CA's certificate (FABRIC-3 XLVI). Run again, it adds what
|
||||
# is missing and changes nothing else. RELEASE_TOKEN: a token that may write the repository.
|
||||
tag=${2:-}; [ -n "$tag" ] || usage
|
||||
: "${RELEASE_TOKEN:?RELEASE_TOKEN: a token that may write the repository}"
|
||||
api=${GITEA_API:-${GITHUB_SERVER_URL:-https://gitea.starshipos-f18.org}/api/v1/repos/${GITHUB_REPOSITORY:-rajames/LithosAnanke}}
|
||||
need "$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$OUT/SHA256SUMS"
|
||||
files=("$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$OUT/SHA256SUMS")
|
||||
for f in "$DOCS"/*.pdf ci/pki/starshipos-root-ca.pem; do [ -f "$f" ] && files+=("$f"); done
|
||||
gitea() { curl -fsS -H "Authorization: token $RELEASE_TOKEN" "$@"; }
|
||||
body=$(python3 -c '
|
||||
import json, sys
|
||||
notes, sums, version = sys.argv[1], open(sys.argv[2]).read(), sys.argv[3]
|
||||
print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.strip() + "\n```\n\n"
|
||||
"Also in Nexus: `org.starship:kernelImage:" + version + "` and `org.starship:documentation:" + version + "`.\n"))' \
|
||||
"$(git tag -l --format='%(contents)' "$tag")" "$OUT/SHA256SUMS" "$VERSION")
|
||||
id=$(gitea "$api/releases/tags/$tag" 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' 2>/dev/null || true)
|
||||
if [ -z "$id" ]; then
|
||||
pre=false; case "$tag" in *-*) pre=true;; esac
|
||||
id=$(gitea -X POST -H 'Content-Type: application/json' "$api/releases" \
|
||||
-d "{\"tag_name\": \"$tag\", \"name\": \"$tag\", \"prerelease\": $pre, \"body\": $body}" |
|
||||
python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
|
||||
echo "=== nightly: Gitea release $tag made (id $id, pre-release $pre)"
|
||||
else
|
||||
echo "=== nightly: Gitea release $tag is there (id $id)"
|
||||
fi
|
||||
have=$(gitea "$api/releases/$id/assets" | python3 -c 'import json,sys; print(" ".join(a["name"] for a in json.load(sys.stdin)))')
|
||||
for f in "${files[@]}"; do
|
||||
n=$(basename "$f")
|
||||
case " $have " in *" $n "*) echo " $n: there"; continue;; esac
|
||||
gitea -o /dev/null -X POST -F "attachment=@$f" "$api/releases/$id/assets?name=$n" && echo " $n: attached"
|
||||
done
|
||||
;;
|
||||
publish-docs)
|
||||
url=${2:-}; [ -n "$url" ] || usage
|
||||
need "$DOCS/book.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf"
|
||||
|
||||
@@ -104,7 +104,7 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c
|
||||
<p style="margin: 0; color: #C8D1DE !important">A conservation invariant that holds regardless of instruction set. Validated across 38,400+ runs in 128 configurations under QEMU on amd64, aarch64 and riscv64, and published on SSRN.</p>
|
||||
</div>
|
||||
<div style="flex: 1 1 220px; display: flex; flex-direction: column; gap: 12px">
|
||||
<a href="https://starshipos-f18.org/" style="display: inline-block; padding: 14px 20px; background: #F5891F !important; color: #0A1222 !important; font-weight: 700; text-decoration: none; border-radius: 4px; text-align: center">Read the paper</a>
|
||||
<a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5930134" style="display: inline-block; padding: 14px 20px; background: #F5891F !important; color: #0A1222 !important; font-weight: 700; text-decoration: none; border-radius: 4px; text-align: center">Read the paper</a>
|
||||
<a href="https://starshipos-f18.org/" style="display: inline-block; padding: 14px 20px; border: 1px solid #34486A !important; color: #EEF2F7 !important; text-decoration: none; border-radius: 4px; text-align: center">Design docs on the .org</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,6 @@ one {{html}} block (which needs a page author with script rights). What changes
|
||||
- Try it: the drawn console becomes the real one, an iframe of try.starshipos-f18.com;
|
||||
- the inquiry form posts to the .org's PublicInquiry, which files it under Inquiries there
|
||||
(admin-only, site/org/inquiries); the announcements form, which has no server, sends by e-mail;
|
||||
- a link the canvas left as a placeholder ([SSRN PAPER URL]) points at the .org until it is given.
|
||||
|
||||
install.sh puts the pages and the images on the wiki.
|
||||
"""
|
||||
@@ -52,7 +51,6 @@ def body(src):
|
||||
html = html.replace('href="https://starshipos.org"', 'href="%s"' % ORG)
|
||||
html = html.replace(">starshipos.org</a>", ">starshipos-f18.org</a>")
|
||||
html = html.replace("on starshipos.org.", "on starshipos-f18.org.")
|
||||
html = html.replace('href="[SSRN PAPER URL]"', 'href="%s"' % ORG)
|
||||
for blob, name in IMAGES.items():
|
||||
html = html.replace('src="%s"' % blob, 'src="/bin/download/Main/WebHome/%s"' % name)
|
||||
html = html.replace("min-height: 100vh", "min-height: 0") # inside the wiki's page, not a screen
|
||||
|
||||
@@ -94,7 +94,7 @@ a{color:#F5891F}a:hover{color:#FFB061}
|
||||
<p style="margin: 0; color: #C8D1DE">A conservation invariant that holds regardless of instruction set. Validated across 38,400+ runs in 128 configurations under QEMU on amd64, aarch64 and riscv64, and published on SSRN.</p>
|
||||
</div>
|
||||
<div style="flex: 1 1 220px; display: flex; flex-direction: column; gap: 12px">
|
||||
<a href="[SSRN PAPER URL]" style="display: inline-block; padding: 14px 20px; background: {{accent}}; color: #0A1222; font-weight: 700; text-decoration: none; border-radius: 4px; text-align: center">Read the paper</a>
|
||||
<a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5930134" style="display: inline-block; padding: 14px 20px; background: {{accent}}; color: #0A1222; font-weight: 700; text-decoration: none; border-radius: 4px; text-align: center">Read the paper</a>
|
||||
<a href="https://starshipos.org" style="display: inline-block; padding: 14px 20px; border: 1px solid #34486A; color: #EEF2F7; text-decoration: none; border-radius: 4px; text-align: center">Design docs on the .org</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user