Updates to hello world.

This commit is contained in:
Storm Dragon committed 2026-10-13 13:35:30 -04:00
1 parent 6a02494ac8
commit 785569abdd
7 files changed
+177 -20

No files matched your search

+1
View File
@@ -1,4 +1,5 @@
/hello-world/bin/
__pycache__/
*.pyc
/dist/
+4 -1
View File
@@ -1,7 +1,7 @@
GOCACHE ?= /tmp/cthulhu-go-cache
export GOCACHE
.PHONY: build check
.PHONY: build check package
build:
go build -buildvcs=false -mod=readonly -o hello-world/bin/hello-world ./hello-world/src
@@ -10,3 +10,6 @@ check: build
go vet ./...
python3 -B tools/check.py
package:
python3 -B tools/package.py
+60 -17
View File
@@ -7,10 +7,10 @@ Repository material and Hello World are licensed under GPL-3.0-or-later; see
`repository.toml` identifies this source. `catalog.toml` explicitly lists its
Tentacles, each in a direct top-level folder with its own `tentacle.toml`.
The catalog currently contains discovery metadata, with no release artifacts.
The proposed source URL is
`https://git.stormux.org/storm/cthulhu-tentacles`; it becomes usable after the
maintainer creates and publishes the Git repository.
The catalog lists verified native packages for Linux amd64 and arm64. Prepared
release artifacts live in `dist/`; they become downloadable only after publication.
The discovery source URL is
`https://git.stormux.org/storm/cthulhu-tentacles`.
## Build and check
@@ -29,20 +29,63 @@ Checks validate the TOML metadata, registration and suggested bindings, and
invoke Hello World through the actual Cthulhu-Go host. They produce no speech
or Braille and leave the running reader alone.
## Discovery and the upcoming installation workflow
## Repository installation
After publishing this repository, open global Cthulhu Preferences → Tentacles
→ Add repository and enter its HTTPS Git URL. Its tab should list Hello World,
version 0.1.0, with the description from its manifest.
With the updated reader, open global Cthulhu Preferences → Tentacles → Add
repository and enter the HTTPS Git URL above. Refresh an already added source.
Select Hello World and press Space to review its source/version and requested
speech/Braille access, then **Install and enable**. **Install** instead downloads
and saves the package disabled. The reader verifies the catalog's SHA-256 digest,
package identity and native architecture before saving a launch entry. Catalogs
without a published compatible release show a clear message and remain unchecked.
No source builds or install scripts run during this workflow. Resource limits
do not make untrusted same-user programs safe.
The current reader validates and displays repository catalogs. It does not
yet install or build a Tentacle from a catalog. Manifest launch/runtime/API
fields document this package for the next installation slice; the current
discovery parser does not interpret them. There is no invented download URL,
release digest, or automatic installation step here.
After installation, refresh status until Active and use Cthulhu+Alt+Shift+H.
Disable retains the package; re-enabling and reader startup use installed files
without network access. Removing the source retains installed Tentacles. Removing
a managed Tentacle stops it and deletes its package files after shutdown.
Package updates are not implemented in this slice. See the
[Hello World live checks](hello-world/README.md#live-verification).
For local execution before installation is implemented, see the explicit
[Hello World launch procedure](hello-world/README.md). Future publishing should
add immutable artifacts and verified digests only once the reader has a defined
installation contract.
## Prepare and publish packages
```sh
make check
make package
```
`tools/package.py` cross-builds statically linked native binaries for Linux amd64
and arm64 from the matching adjacent SDK. It creates deterministic archives
containing `tentacle.toml`, `bin/hello-world`, `README.md` and `LICENSE`, plus
`dist/SHA256SUMS` and a generated `dist/catalog.toml`. Go builds use readonly
module metadata, trim paths and omit VCS stamping. To build one architecture:
```sh
python3 -B tools/package.py --architecture amd64
```
Validate the actual host-architecture package through Cthulhu-Go's installer
and command host, without changing the running reader:
```sh
cd ../cthulhu-go
CTHULHU_TENTACLE_RELEASE_ROOT=../cthulhu-tentacles GOCACHE=/tmp/cthulhu-go-cache go test ./internal/tentacle -run '^TestBuiltHelloWorldPackageThroughHost$' -count=1 -v
```
Before publication, review both repositories' changes and contributing guides.
Commit the reviewed source with the matching SDK available; select that source
revision for the `hello-world-v0.1.0` release. In the Git hosting release page,
upload `dist/hello-world-0.1.0-linux-amd64.tar.gz`,
`dist/hello-world-0.1.0-linux-arm64.tar.gz` and `dist/SHA256SUMS`. Publish and verify
the download URLs and digests **before** publishing the matching catalog change.
Copy the reviewed `dist/catalog.toml` to `catalog.toml`, review the diff, and
submit/push it explicitly. A release filename/version must never be reused for
changed content; bump the version before preparing a later release.
The package command creates local artifacts only. It does not create commits,
releases, tags, uploads or pushes. Prepared catalog changes are not evidence that
the corresponding release is already available. Live shortcut, speech, Braille,
lock/handoff and interruption checks remain necessary after publication and an
explicit reader restart. For source-only local testing, the development launch
procedure remains in [Hello World](hello-world/README.md#local-launch).
+11
View File
@@ -4,3 +4,14 @@ format-version = 1
id = "hello-world"
manifest = "hello-world/tentacle.toml"
[[tentacles.releases]]
platform = "linux"
architecture = "amd64"
url = "https://git.stormux.org/storm/cthulhu-tentacles/releases/download/hello-world-v0.1.0/hello-world-0.1.0-linux-amd64.tar.gz"
sha256 = "2bad533bc217a5b1bd374115f6151da2a1f6e4ccdbdc93528a5a783f0518f8cd"
[[tentacles.releases]]
platform = "linux"
architecture = "arm64"
url = "https://git.stormux.org/storm/cthulhu-tentacles/releases/download/hello-world-v0.1.0/hello-world-0.1.0-linux-arm64.tar.gz"
sha256 = "9ef956a04fb1fa98ac01b04fadc122f92b77d708440e0438a7451da62078a9d4"
+9 -1
View File
@@ -20,6 +20,14 @@ Hello World requests presentation access only. It receives no focused text,
uses no network services, and stores no settings. Source and distribution are
licensed under GPL-3.0-or-later; see [LICENSE](LICENSE).
## Repository installation
Refresh the source in global Preferences → Tentacles. Space on Hello World
opens first-use review and installs/enables the package after acceptance.
**Install** saves it disabled instead. The release must be published at the
catalog URL; building locally alone does not make it downloadable. Refresh
status until Active before trying the shortcut.
## Local launch
Run `make build` from the repository root. Rebuild Cthulhu-Go with the matching
@@ -72,7 +80,7 @@ Resource limits do not make untrusted same-user programs safe.
5. Check speech off/beeps and disabled Braille settings. Hello World must respect
them. Remap or occupy the suggested binding and check core/user precedence.
Audible speech, physical shortcuts, actual display output and the eventual
Audible speech, physical shortcuts, actual display output and the
repository-installation workflow require these live checks; protocol tests do
not establish them.
+21 -1
View File
@@ -3,11 +3,13 @@
"""Check the repository and the built Tentacle without running a reader."""
import json
import hashlib
import os
from pathlib import Path
import select
import subprocess
import tomllib
from urllib.parse import urlparse
def require(condition, message):
@@ -27,7 +29,8 @@ def check_metadata(repoRoot):
"Unsupported metadata format")
require(repository["id"] == "storm.cthulhu-tentacles" and repository["name"],
"Repository identity is missing")
require(catalog["tentacles"] == [{"id": "hello-world", "manifest": "hello-world/tentacle.toml"}],
require(len(catalog["tentacles"]) == 1 and catalog["tentacles"][0]["id"] == "hello-world"
and catalog["tentacles"][0]["manifest"] == "hello-world/tentacle.toml",
"Catalog must list exactly Hello World")
manifest = read_toml(repoRoot / catalog["tentacles"][0]["manifest"])
require(manifest["format-version"] == 1 and manifest["id"] == "hello-world"
@@ -38,6 +41,23 @@ def check_metadata(repoRoot):
require((repoRoot / "LICENSE").is_file() and (repoRoot / "hello-world/LICENSE").is_file(),
"Repository and Tentacle licenses are required")
seen = set()
for release in catalog["tentacles"][0].get("releases", []):
key = (release["platform"], release["architecture"])
require(key not in seen and key[0] == "linux" and key[1] in ("amd64", "arm64"),
"Invalid or duplicate release target")
seen.add(key)
parsed = urlparse(release["url"])
require(parsed.scheme == "https" and parsed.hostname and not parsed.username
and not parsed.fragment, "Release URL must be HTTPS without credentials")
digest = release["sha256"]
require(len(digest) == 64 and all(character in "0123456789abcdef" for character in digest),
"Invalid SHA-256 digest")
artifactPath = repoRoot / "dist" / parsed.path.rsplit("/", 1)[-1]
if artifactPath.exists():
require(hashlib.sha256(artifactPath.read_bytes()).hexdigest() == digest,
"Prepared package does not match its catalog digest")
def check_registration(programPath):
with subprocess.Popen([str(programPath)], stdin=subprocess.PIPE,
+71
View File
@@ -0,0 +1,71 @@
# Copyright (c) 2026 Storm Dragon
# SPDX-License-Identifier: GPL-3.0-or-later
"""Build reproducible Hello World release artifacts and a catalog for review."""
import argparse
import gzip
import hashlib
import io
import json
import os
from pathlib import Path
import subprocess
import tarfile
import tomllib
def build_package(repoRoot, architecture, version):
distRoot = repoRoot / "dist"
distRoot.mkdir(exist_ok=True)
programPath = distRoot / f"hello-world-linux-{architecture}"
environment = dict(os.environ, CGO_ENABLED="0", GOOS="linux", GOARCH=architecture)
environment.setdefault("GOCACHE", "/tmp/cthulhu-go-cache")
subprocess.run(["go", "build", "-buildvcs=false", "-mod=readonly", "-trimpath",
"-ldflags=-s -w", "-o", str(programPath), "./hello-world/src"],
cwd=repoRoot, env=environment, check=True)
artifactPath = distRoot / f"hello-world-{version}-linux-{architecture}.tar.gz"
with artifactPath.open("wb") as output:
with gzip.GzipFile(filename="", mode="wb", fileobj=output, mtime=0) as compressed:
with tarfile.open(fileobj=compressed, mode="w", format=tarfile.USTAR_FORMAT) as archive:
for name, data, mode in [
("tentacle.toml", (repoRoot / "hello-world/tentacle.toml").read_bytes(), 0o644),
("README.md", (repoRoot / "hello-world/README.md").read_bytes(), 0o644),
("LICENSE", (repoRoot / "hello-world/LICENSE").read_bytes(), 0o644),
("bin/hello-world", programPath.read_bytes(), 0o755),
]:
member = tarfile.TarInfo(name)
member.size, member.mode, member.mtime = len(data), mode, 0
archive.addfile(member, io.BytesIO(data))
digest = hashlib.sha256(artifactPath.read_bytes()).hexdigest()
return artifactPath, digest
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--architecture", nargs="+", choices=["amd64", "arm64"], default=["amd64", "arm64"])
args = parser.parse_args()
if len(set(args.architecture)) != len(args.architecture):
parser.error("Architectures must not be repeated")
repoRoot = Path(__file__).resolve().parents[1]
manifest = tomllib.loads((repoRoot / "hello-world/tentacle.toml").read_text())
version = manifest["version"]
if not version or any(character not in "0123456789abcdefghijklmnopqrstuvwxyz.-" for character in version):
raise ValueError("Version must be safe for release filenames")
lines = ["format-version = 1", "", "[[tentacles]]", 'id = "hello-world"',
'manifest = "hello-world/tentacle.toml"']
checksums = []
for architecture in args.architecture:
artifactPath, digest = build_package(repoRoot, architecture, version)
releaseURL = f"https://git.stormux.org/storm/cthulhu-tentacles/releases/download/hello-world-v{version}/{artifactPath.name}"
lines += ["", "[[tentacles.releases]]", 'platform = "linux"',
f"architecture = {json.dumps(architecture)}", f"url = {json.dumps(releaseURL)}",
f"sha256 = {json.dumps(digest)}"]
checksums.append(f"{digest} {artifactPath.name}")
print(f"Prepared {artifactPath.name}: {digest}")
(repoRoot / "dist/catalog.toml").write_text("\n".join(lines) + "\n")
(repoRoot / "dist/SHA256SUMS").write_text("\n".join(checksums) + "\n")
print("Prepared dist/catalog.toml and dist/SHA256SUMS for review. Nothing was published.")
if __name__ == "__main__":
main()