Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,26 @@ edit/test loop.

## Install (user toolchain)

**Prebuilt** (latest GitHub release for your platform):
Published builds are **prereleases**. The current tag is
[`v0.0.1-alpha.9`](https://github.com/modoterra/echo/releases/tag/v0.0.1-alpha.9)
and ships `xo-linux-x86_64.tar.gz` and `xo-macos-arm64.tar.gz`.
`from-release` with no tag installs the newest published prerelease. Pass a tag
to pin. GitHub `/releases/latest` only resolves a non-prerelease and 404s today.

```bash
curl -fsSL https://raw.githubusercontent.com/modoterra/echo/main/scripts/install.sh \
| bash -s -- from-release

# Pin this tag
# … | bash -s -- from-release v0.0.1-alpha.9
```

From a checkout, build + install under XDG and link `~/.local/bin/xo`:

```bash
./scripts/install.sh # build from this tree
./scripts/install.sh from-release # or use a published release tarball
./scripts/install.sh from-release # newest published prerelease
./scripts/install.sh from-release v0.0.1-alpha.9
./scripts/install.sh upgrade # new version, keep previous
./scripts/uninstall.sh # remove toolchain ( --purge also clears $XO_HOME )
./scripts/install.sh doctor
Expand Down Expand Up @@ -120,6 +128,9 @@ GitHub release is published** — not on push, PR, or bare tags.
| Windows x86_64 | `windows-2022` |
| macOS arm64 | `macos-14` |

The current published tag (`v0.0.1-alpha.9`) attaches `xo-linux-x86_64` and
`xo-macos-arm64` only. A Windows tarball is not on that tag.

On Linux, smoke (`cargo test -p xo`, `xo run` hello) and **`scripts/gate echo26`**
(Echo 2026 conformance) are hard gates when that workflow runs.

Expand Down
4 changes: 3 additions & 1 deletion docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ version # release tag
```

Users install with `scripts/install.sh from-release` (see
[`docs/install.md`](install.md)).
[`docs/install.md`](install.md)). The current published tag is a prerelease
and may not include every matrix artifact; `/install` lists what that tag
attached.

Each job installs **LLVM 22** from **official**
[`llvm/llvm-project` release tarballs](https://github.com/llvm/llvm-project/releases)
Expand Down
36 changes: 21 additions & 15 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,38 @@ User-facing install layout for the **xo** toolchain. Aligns with

## Quick start (prebuilt — recommended)

Install the **latest GitHub release** for your platform (no Rust toolchain required
for the install itself; `clang` is still needed to **run/build** Echo programs):
Published builds are **prereleases**. The current tag is
[`v0.0.1-alpha.9`](https://github.com/modoterra/echo/releases/tag/v0.0.1-alpha.9).
`from-release` with no tag installs the newest published prerelease (including
alphas). Pass a tag to pin. GitHub `/releases/latest` only resolves a
non-prerelease and 404s today.

No Rust toolchain is required for the install itself; `clang` is still needed
to **run/build** Echo programs.

```bash
curl -fsSL https://raw.githubusercontent.com/modoterra/echo/main/scripts/install.sh \
| bash -s -- from-release

# Pin a tag
# Pin this tag
curl -fsSL https://raw.githubusercontent.com/modoterra/echo/main/scripts/install.sh \
| bash -s -- from-release v0.0.1-alpha.1
| bash -s -- from-release v0.0.1-alpha.9

# From a checkout
./scripts/install.sh from-release
./scripts/install.sh doctor
```

Prebuilt platforms (CI release assets `xo-<artifact>.tar.gz`):
Assets on `v0.0.1-alpha.9`:

| Artifact | Host |
|----------|------|
| `linux-x86_64` | Linux x86_64 |
| `macos-arm64` | Apple Silicon |
| `windows-x86_64` | Windows x86_64 (tarball; use WSL or unpack manually if preferred) |
| Archive | Host |
|---------|------|
| `xo-linux-x86_64.tar.gz` | Linux x86_64 |
| `xo-macos-arm64.tar.gz` | Apple Silicon |

Each archive contains `bin/xo`, `bin/libecho_runtime.a` (when produced), and
`std/`. Assets are attached when a GitHub release is published (see `docs/ci.md`).
A Windows tarball is not on this tag. Each archive contains `bin/xo`,
`bin/libecho_runtime.a` (when produced), and `std/`. See `docs/ci.md` for the
release workflow matrix.

Ensure `~/.local/bin` (or `$XO_BIN_DIR`) is on your `PATH`.

Expand Down Expand Up @@ -120,9 +126,9 @@ Defaults: `~/.local/state/xo`, `~/.config/xo`.
# From checkout: rebuild + install new version dir
./scripts/install.sh upgrade

# Prebuilt: re-fetch latest (or tag) release
# Prebuilt: re-fetch newest published prerelease (or pin a tag)
./scripts/install.sh from-release
# ECHO_RELEASE=v0.0.2 ./scripts/install.sh from-release
# ECHO_RELEASE=v0.0.1-alpha.9 ./scripts/install.sh from-release
```

Upgrade path:
Expand Down Expand Up @@ -167,7 +173,7 @@ Uninstall removes:
| `XO_BIN_DIR` | Directory for the `xo` PATH link |
| `XO_INSTALL_ROOT` | Extra std package root (optional) |
| `ECHO_REPO` | GitHub `owner/name` for prebuilts (default `modoterra/echo`) |
| `ECHO_RELEASE` | Release tag or `latest` for `from-release` |
| `ECHO_RELEASE` | Release tag for `from-release` (unset = newest published prerelease) |
| `ECHO_VERSION` / `XO_VERSION` | Toolchain version directory name |
| `GITHUB_TOKEN` / `GH_TOKEN` | Optional auth for GitHub API / downloads |
| `CARGO_PROFILE` | `release` (default) or `debug` (checkout builds) |
Expand Down
51 changes: 15 additions & 36 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Usage:
# ./scripts/install.sh # build from checkout (default when in repo)
# ./scripts/install.sh install
# ./scripts/install.sh from-release [tag] # latest (or tag) GitHub release
# ./scripts/install.sh from-release [tag] # newest published prerelease (or pin a tag)
# ./scripts/install.sh upgrade # rebuild + flip current (checkout)
# ./scripts/install.sh uninstall [--purge]
# ./scripts/install.sh doctor
Expand Down Expand Up @@ -172,24 +172,6 @@ need_cmd() {
}

# JSON helpers without requiring jq (keep install self-contained).
json_string_field() {
# json_string_field <field> — reads JSON object on stdin, prints unescaped string or empty.
local field="$1"
# shellcheck disable=SC2016
python3 -c '
import json, sys
field = sys.argv[1]
data = json.load(sys.stdin)
val = data.get(field)
if val is None:
sys.exit(0)
if isinstance(val, str):
print(val)
else:
print(val)
' "$field" 2>/dev/null || true
}

github_api() {
local url="$1"
local args=(-fsSL -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28")
Expand All @@ -214,15 +196,11 @@ resolve_release_asset() {
need_cmd tar

if [[ -z "$want_tag" || "$want_tag" == "latest" ]]; then
# Prefer /releases/latest (stable). If missing (404) or empty assets, use newest
# published release including prereleases (current alpha cadence).
if json="$(github_api "${api_base}/releases/latest" 2>/dev/null)" && [[ -n "$json" ]]; then
tag="$(printf '%s' "$json" | json_string_field tag_name)"
fi
if [[ -z "${tag:-}" ]]; then
json="$(github_api "${api_base}/releases?per_page=10")"
tag="$(
printf '%s' "$json" | python3 -c '
# Newest published release, including prereleases. GitHub /releases/latest
# only returns a non-prerelease and 404s while every Echo tag is still an alpha.
json="$(github_api "${api_base}/releases?per_page=10")"
tag="$(
printf '%s' "$json" | python3 -c '
import json, sys
rels = json.load(sys.stdin)
for r in rels:
Expand All @@ -231,11 +209,9 @@ for r in rels:
print(r["tag_name"])
break
'
)"
json="$(github_api "${api_base}/releases/tags/${tag}")"
else
json="$(github_api "${api_base}/releases/tags/${tag}")"
fi
)"
[[ -n "$tag" ]] || die "could not resolve a GitHub release for ${ECHO_REPO}"
json="$(github_api "${api_base}/releases/tags/${tag}")"
else
tag="$want_tag"
json="$(github_api "${api_base}/releases/tags/${tag}")"
Expand Down Expand Up @@ -624,7 +600,7 @@ Echo / xo installer (XDG)
Usage:
scripts/install.sh [install] From checkout: build + install
Without checkout: same as from-release
scripts/install.sh from-release [tag] Install prebuilt from GitHub release
scripts/install.sh from-release [tag] Newest published prerelease, or pin a tag
scripts/install.sh upgrade New version (build or from-release)
scripts/install.sh uninstall [--purge]
scripts/install.sh doctor Show paths and install status
Expand All @@ -634,7 +610,10 @@ One-liner (no git clone):
curl -fsSL https://raw.githubusercontent.com/modoterra/echo/main/scripts/install.sh \
| bash -s -- from-release

Prebuilt platforms (CI): linux-x86_64, macos-arm64, windows-x86_64 (tarball).
# Pin a tag
# … | bash -s -- from-release v0.0.1-alpha.9

Current prerelease (v0.0.1-alpha.9) assets: xo-linux-x86_64, xo-macos-arm64.

Environment:
XO_HOME User .xo root (packages); default $XDG_CACHE_HOME/.xo
Expand All @@ -643,7 +622,7 @@ Environment:
XDG_CONFIG_HOME Config (…/xo)
XO_BIN_DIR Where to place the xo PATH link (default ~/.local/bin)
ECHO_REPO GitHub owner/name (default modoterra/echo)
ECHO_RELEASE Release tag or "latest" (default latest)
ECHO_RELEASE Release tag, or newest published prerelease when unset
ECHO_VERSION / XO_VERSION Force toolchain version directory name
CARGO_PROFILE release (default) or debug — checkout builds only
GITHUB_TOKEN / GH_TOKEN Optional; higher API rate limits
Expand Down
73 changes: 72 additions & 1 deletion www/scripts/verify-docs-pages.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
* - every language-feature catalog entry is a real page with a summary
* and at least one Echo code block
*
* Loads src/docs/site.ts and src/docs/content.ts through Vite SSR.
* Loads src/docs/site.ts, src/docs/content.ts, and src/lib/current-release.ts
* through Vite SSR. Also checks that /install, README, and docs/install.md
* name the current prerelease and its real assets.
*/
import { createServer } from "vite";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import path from "node:path";

Expand All @@ -24,6 +27,7 @@ const server = await createServer({
try {
const site = await server.ssrLoadModule("/src/docs/site.ts");
const content = await server.ssrLoadModule("/src/docs/content.ts");
const release = await server.ssrLoadModule("/src/lib/current-release.ts");

const {
docsHubCatalog,
Expand Down Expand Up @@ -135,6 +139,73 @@ try {
}
}

const { currentPrereleaseTag, currentPrereleaseAssets, currentPrereleaseUrl, releasesIndexUrl } =
release;
if (currentPrereleaseTag !== "v0.0.1-alpha.9") {
fail(`currentPrereleaseTag should be v0.0.1-alpha.9, got ${currentPrereleaseTag}`);
}
const artifactIds = currentPrereleaseAssets.map((asset) => asset.artifact);
if (artifactIds.join(",") !== "linux-x86_64,macos-arm64") {
fail(`current prerelease assets should be linux-x86_64 and macos-arm64, got ${artifactIds}`);
}
if (currentPrereleaseAssets.some((asset) => /windows/i.test(asset.artifact + asset.archive))) {
fail("current prerelease must not claim a Windows tarball");
}
if (!currentPrereleaseUrl.endsWith(`/releases/tag/${currentPrereleaseTag}`)) {
fail("currentPrereleaseUrl must point at the current tag, not /releases/latest");
}
if (releasesIndexUrl.endsWith("/releases/latest")) {
fail("releasesIndexUrl must not be /releases/latest");
}

const repoRoot = path.resolve(root, "..");
const installPage = readFileSync(path.join(root, "src/install.tsx"), "utf8");
const readme = readFileSync(path.join(repoRoot, "README.md"), "utf8");
const installDoc = readFileSync(path.join(repoRoot, "docs/install.md"), "utf8");
const installSh = readFileSync(path.join(repoRoot, "scripts/install.sh"), "utf8");
if (
!installPage.includes("./lib/current-release") ||
!installPage.includes("currentPrereleaseTag")
) {
fail("src/install.tsx must render the current prerelease from current-release.ts");
}
if (!installPage.includes("from-release") || !installPage.includes("currentPrereleaseTag")) {
fail("install page must show from-release and how to pin the current tag");
}
if (/windows-x86_64/.test(installPage)) {
fail("src/install.tsx must not claim a Windows tarball");
}
if (!/prerelease/i.test(installPage) || !/prerelease/i.test(readme)) {
fail("install page and README must say published builds are prereleases");
}
for (const [label, text] of [
["README.md", readme],
["docs/install.md", installDoc],
["scripts/install.sh", installSh],
]) {
if (!text.includes(currentPrereleaseTag)) {
fail(`${label} must name ${currentPrereleaseTag}`);
}
if (!text.includes("xo-linux-x86_64") || !text.includes("xo-macos-arm64")) {
fail(`${label} must list xo-linux-x86_64 and xo-macos-arm64`);
}
if (/latest GitHub release/i.test(text)) {
fail(`${label} must not present a GitHub latest release`);
}
if (/releases\/latest/.test(text) && !/404/.test(text)) {
fail(`${label} must not present /releases/latest as working`);
}
}
if (/latest GitHub release/i.test(installPage)) {
fail("src/install.tsx must not present a GitHub latest release");
}
if (/releases\/latest/.test(installPage) && !/404/.test(installPage)) {
fail("src/install.tsx must not present /releases/latest as working");
}
if (!installSh.includes("releases?per_page=")) {
fail("install.sh from-release must list published releases, including prereleases");
}

const snapshot = renderStaticHomeAndHub();
if (snapshot.includes("modoterra.github.io")) {
fail("static homepage must not point at modoterra.github.io");
Expand Down
2 changes: 1 addition & 1 deletion www/src/docs/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export const docsHubCatalog: DocsCatalogGroup[] = [
{
title: "Install",
to: "/install",
description: "Build xo from a checkout and put it on your PATH.",
description: "Install the current xo prerelease, or build from a checkout.",
},
{
title: "First program",
Expand Down
Loading
Loading