diff --git a/README.md b/README.md index 451fa903..7194fc7a 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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. diff --git a/docs/ci.md b/docs/ci.md index e2fae8ca..fc44ae0b 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -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) diff --git a/docs/install.md b/docs/install.md index f0425834..5ac021b1 100644 --- a/docs/install.md +++ b/docs/install.md @@ -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-.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`. @@ -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: @@ -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) | diff --git a/scripts/install.sh b/scripts/install.sh index 521af227..02e1999b 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -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 @@ -172,24 +172,6 @@ need_cmd() { } # JSON helpers without requiring jq (keep install self-contained). -json_string_field() { - # json_string_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") @@ -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: @@ -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}")" @@ -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 @@ -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 @@ -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 diff --git a/www/scripts/verify-docs-pages.mjs b/www/scripts/verify-docs-pages.mjs index f311fa2a..bc070964 100644 --- a/www/scripts/verify-docs-pages.mjs +++ b/www/scripts/verify-docs-pages.mjs @@ -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"; @@ -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, @@ -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"); diff --git a/www/src/docs/site.ts b/www/src/docs/site.ts index 2d2c8823..3bc77591 100644 --- a/www/src/docs/site.ts +++ b/www/src/docs/site.ts @@ -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", diff --git a/www/src/install.tsx b/www/src/install.tsx index fa109def..5e18559b 100644 --- a/www/src/install.tsx +++ b/www/src/install.tsx @@ -1,13 +1,19 @@ import { Link } from "@tanstack/react-router"; import { CtaLink } from "./components/cta-link"; import { InstallSnippet } from "./components/install-snippet"; - -const PREBUILT_INSTALL = `# Latest GitHub release for this machine (linux-x86_64 / macos-arm64) +import { + currentPrereleaseTag, + currentPrereleaseAssets, + currentPrereleaseUrl, + releasesIndexUrl, +} from "./lib/current-release"; + +const PREBUILT_INSTALL = `# Newest published prerelease for this machine (linux-x86_64 / macos-arm64) curl -fsSL https://raw.githubusercontent.com/modoterra/echo/main/scripts/install.sh \\ | bash -s -- from-release -# Pin a tag -# … | bash -s -- from-release v0.0.1-alpha.1 +# Pin this tag +# … | bash -s -- from-release ${currentPrereleaseTag} xo --help xo doctor 2>/dev/null || true`; @@ -21,8 +27,9 @@ const USER_INSTALL = `# From the checkout: release build + XDG install + ~/.loca ./scripts/install.sh ./scripts/install.sh doctor -# Or install the published release tarball without a local build +# Newest published prerelease, or pin a tag ./scripts/install.sh from-release +./scripts/install.sh from-release ${currentPrereleaseTag} # Upgrade (keeps prior toolchain dirs) ./scripts/install.sh upgrade @@ -38,7 +45,7 @@ xo run examples/misc/hello.echo ./target/debug/xo run examples/misc/sum_list.echo`; /** - * Product install page: prebuilt release path + source build fallback. + * Product install page: current prerelease assets + source build fallback. */ export function InstallPage() { return ( @@ -48,9 +55,16 @@ export function InstallPage() { Install Echo

- The public CLI is xo. Take - a prebuilt from the latest GitHub release when you only need to run programs. Build from - source when you edit the compiler. + The public CLI is xo. + Published builds are prereleases. The current tag is{" "} + + {currentPrereleaseTag} + + . Take a prebuilt when you only need to run programs. Build from source when you edit the + compiler.

@@ -58,12 +72,40 @@ export function InstallPage() { Prebuilt (recommended)

- The script downloads xo,{" "} + {currentPrereleaseTag} ships{" "} + {currentPrereleaseAssets.map((asset, index) => ( + + {index > 0 ? " and " : ""} + {asset.archive} + + ))} + . The script downloads xo,{" "} libecho_runtime.a, and{" "} - std/ for your platform, then links{" "} + std/ for that host, then links{" "} ~/.local/bin/xo. - Published builds cover Linux x86_64 and macOS arm64.

+

+ 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. See the{" "} + + releases list + + . +

+
    + {currentPrereleaseAssets.map((asset) => ( +
  • + {asset.archive} + {" · "} + {asset.host} +
  • + ))} +
diff --git a/www/src/lib/current-release.ts b/www/src/lib/current-release.ts new file mode 100644 index 00000000..82a748f5 --- /dev/null +++ b/www/src/lib/current-release.ts @@ -0,0 +1,25 @@ +/** + * Current published Echo toolchain prerelease. + * + * GitHub `/releases/latest` only resolves a non-prerelease and 404s while every + * tag is still an alpha. Keep this file, `/install`, README, and docs/install.md + * aligned when a new tag is published. List only assets on that tag. + */ +export const currentPrereleaseTag = "v0.0.1-alpha.9"; + +export const currentPrereleaseAssets = [ + { + artifact: "linux-x86_64", + archive: "xo-linux-x86_64.tar.gz", + host: "Linux x86_64", + }, + { + artifact: "macos-arm64", + archive: "xo-macos-arm64.tar.gz", + host: "macOS arm64", + }, +] as const; + +export const currentPrereleaseUrl = `https://github.com/modoterra/echo/releases/tag/${currentPrereleaseTag}`; + +export const releasesIndexUrl = "https://github.com/modoterra/echo/releases";