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/ /hello-world/bin/
__pycache__/ __pycache__/
*.pyc *.pyc
/dist/
+4 -1
View File
@@ -1,7 +1,7 @@
GOCACHE ?= /tmp/cthulhu-go-cache GOCACHE ?= /tmp/cthulhu-go-cache
export GOCACHE export GOCACHE
.PHONY: build check .PHONY: build check package
build: build:
go build -buildvcs=false -mod=readonly -o hello-world/bin/hello-world ./hello-world/src go build -buildvcs=false -mod=readonly -o hello-world/bin/hello-world ./hello-world/src
@@ -10,3 +10,6 @@ check: build
go vet ./... go vet ./...
python3 -B tools/check.py 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 `repository.toml` identifies this source. `catalog.toml` explicitly lists its
Tentacles, each in a direct top-level folder with its own `tentacle.toml`. Tentacles, each in a direct top-level folder with its own `tentacle.toml`.
The catalog currently contains discovery metadata, with no release artifacts. The catalog lists verified native packages for Linux amd64 and arm64. Prepared
The proposed source URL is release artifacts live in `dist/`; they become downloadable only after publication.
`https://git.stormux.org/storm/cthulhu-tentacles`; it becomes usable after the The discovery source URL is
maintainer creates and publishes the Git repository. `https://git.stormux.org/storm/cthulhu-tentacles`.
## Build and check ## 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 invoke Hello World through the actual Cthulhu-Go host. They produce no speech
or Braille and leave the running reader alone. 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 With the updated reader, open global Cthulhu Preferences → Tentacles → Add
→ Add repository and enter its HTTPS Git URL. Its tab should list Hello World, repository and enter the HTTPS Git URL above. Refresh an already added source.
version 0.1.0, with the description from its manifest. 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 After installation, refresh status until Active and use Cthulhu+Alt+Shift+H.
yet install or build a Tentacle from a catalog. Manifest launch/runtime/API Disable retains the package; re-enabling and reader startup use installed files
fields document this package for the next installation slice; the current without network access. Removing the source retains installed Tentacles. Removing
discovery parser does not interpret them. There is no invented download URL, a managed Tentacle stops it and deletes its package files after shutdown.
release digest, or automatic installation step here. 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 ## Prepare and publish packages
[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.
```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" id = "hello-world"
manifest = "hello-world/tentacle.toml" 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 uses no network services, and stores no settings. Source and distribution are
licensed under GPL-3.0-or-later; see [LICENSE](LICENSE). 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 ## Local launch
Run `make build` from the repository root. Rebuild Cthulhu-Go with the matching 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 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. 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 repository-installation workflow require these live checks; protocol tests do
not establish them. not establish them.
+21 -1
View File
@@ -3,11 +3,13 @@
"""Check the repository and the built Tentacle without running a reader.""" """Check the repository and the built Tentacle without running a reader."""
import json import json
import hashlib
import os import os
from pathlib import Path from pathlib import Path
import select import select
import subprocess import subprocess
import tomllib import tomllib
from urllib.parse import urlparse
def require(condition, message): def require(condition, message):
@@ -27,7 +29,8 @@ def check_metadata(repoRoot):
"Unsupported metadata format") "Unsupported metadata format")
require(repository["id"] == "storm.cthulhu-tentacles" and repository["name"], require(repository["id"] == "storm.cthulhu-tentacles" and repository["name"],
"Repository identity is missing") "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") "Catalog must list exactly Hello World")
manifest = read_toml(repoRoot / catalog["tentacles"][0]["manifest"]) manifest = read_toml(repoRoot / catalog["tentacles"][0]["manifest"])
require(manifest["format-version"] == 1 and manifest["id"] == "hello-world" 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(), require((repoRoot / "LICENSE").is_file() and (repoRoot / "hello-world/LICENSE").is_file(),
"Repository and Tentacle licenses are required") "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): def check_registration(programPath):
with subprocess.Popen([str(programPath)], stdin=subprocess.PIPE, 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()