From e9d85d0a24f8dafb81232fa816fac703bdd9786a Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Thu, 13 Aug 2026 12:55:16 +0800 Subject: [PATCH 1/6] Cache mise tools in ghcr.io --- .../actions/install-mise-tools/action.yaml | 39 ++++++ .github/workflows/check.yaml | 3 + .github/workflows/docker-linux.yaml | 37 ++++-- .github/workflows/docker-windows.yaml | 12 +- .github/workflows/test.yaml | 10 ++ .mise/config.maint.toml | 113 +++++++++++++++++- .mise/config.toml | 39 ++++++ .mise/config.zig.toml | 4 +- 8 files changed, 238 insertions(+), 19 deletions(-) diff --git a/.github/actions/install-mise-tools/action.yaml b/.github/actions/install-mise-tools/action.yaml index f1ef479..e0b61d2 100644 --- a/.github/actions/install-mise-tools/action.yaml +++ b/.github/actions/install-mise-tools/action.yaml @@ -33,6 +33,45 @@ runs: with: install-action-tools: ${{ inputs.install-action-tools }} + # Seed the mise data dir from this platform's GHCR store before installing. + # Every job used to cold-install every tool, and each github:/aqua tool costs api.github.com calls + # (version lists, artifact-attestation verification) against the repo installation's shared 5000/hr + # bucket; a push's ~30 parallel jobs drained it and `mise install` died with + # + # mise ERROR Failed to install github:benhoyt/goawk@latest: GitHub artifact attestations + # verification error ...: API error: GitHub API returned 403 Forbidden + # "message": "API rate limit exceeded for installation. ..." + # + # observed on commit 5c08a759202d166ac9fac48f5e54ec998d5fcb6e at + # https://github.com/edge-toolkit/core/actions/runs/31659062139/job/94319942583. The store is one OCI + # image per platform, published exclusively by a maintainer running `mise run push-mise-tools` on a + # machine of that platform -- CI only ever reads (no workflow holds packages:write), so a compromised + # workflow cannot poison the store. The image is flattened into the data dir with `crane export`; the + # `mise install` that follows then verifies mostly-installed tools with near-zero API traffic. Any + # restore failure (store not yet published, or a token that can't read it) falls through to the plain + # cold install. + - name: Restore mise tools from GHCR + shell: bash --noprofile --norc -euo pipefail {0} + env: + GH_TOKEN: ${{ inputs.github-token }} + run: | + # CI dogfoods the same `pull-mise-tools` task local devs run, not its own copy of the pull logic. + # The repo's `task.run_auto_install = false` setting means running the task does NOT first + # cold-install every missing tool of the loaded envs -- so crane, which the task body needs, is + # installed individually up front. (An explicit install also dodges a `mise x` quirk: it has been + # seen exec-failing on the very call that triggers a tool's install -- "couldn't exec process: No + # such file or directory".) + mise install crane + # crane's login writes the standard ~/.docker/config.json for the task's pull. + # printf-pipe rather than a `<<<` herestring: this step's declared bash has herestrings, but the + # repo's Windows task shell (busybox ash) does not, and this line is the kind that gets copied. + printf %s "$GH_TOKEN" | mise x crane -- crane auth login ghcr.io -u "$GITHUB_ACTOR" --password-stdin + if mise run pull-mise-tools; then + echo "restored this platform's mise-tools store" + else + echo "store not restorable (not yet published, or unreadable with this token); cold install" + fi + - name: Install mise tools id: install-mise-tools continue-on-error: true diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index f901b6e..bfe02c6 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -8,6 +8,9 @@ name: check permissions: id-token: write contents: write + # Read-only pull of the maintainer-published ghcr.io mise-tools store (install-mise-tools restore step). + # Never write: the stores are published only from a maintainer's machine via `mise run push-mise-tools`. + packages: read # this allows a subsequently queued workflow run to interrupt previous runs in pull requests only concurrency: diff --git a/.github/workflows/docker-linux.yaml b/.github/workflows/docker-linux.yaml index e64bdf4..9e299ee 100644 --- a/.github/workflows/docker-linux.yaml +++ b/.github/workflows/docker-linux.yaml @@ -143,12 +143,30 @@ jobs: # /var stragglers); `find ... -size +50M` for the individual heavyweights. Run in a throwaway container # so the host's writable layer doesn't tilt the numbers; find/du stderr noise (unreadable /proc entries # etc.) prints to the log but doesn't affect the sort pipeline. - echo "::group::et-test: largest dirs (depth 2, >=10M)" - docker run --rm et-test sh -c "du -h --threshold=10M --max-depth=2 / | sort -h | tail -50" - echo "::endgroup::" - echo "::group::et-test: individual files >50M" - docker run --rm et-test sh -c "find / -xdev -type f -size +50M -exec du -h {} + | sort -h | tail -50" - echo "::endgroup::" + # + # Both runs are gated on the tag existing locally: this step is `always()`, and when the build died + # before tagging, a bare `docker run et-test` falls through to pulling `et-test:latest` from Docker + # Hub -- failing this debug step with the misleading + # + # docker: Error response from daemon: pull access denied for et-test, repository does not exist + # or may require 'docker login': denied: requested access to the resource is denied + # + # on top of the real build error (observed on commit 5c08a759202d166ac9fac48f5e54ec998d5fcb6e at + # https://github.com/edge-toolkit/core/actions/runs/31659062139/job/94319942583), and handing + # execution to whoever squats that name on Docker Hub if the pull ever resolved. `--pull=never` + # keeps the registry unreachable even if the gate is edited away. + if docker image inspect et-test >/dev/null 2>&1; then + echo "::group::et-test: largest dirs (depth 2, >=10M)" + du_cmd="du -h --threshold=10M --max-depth=2 / | sort -h | tail -50" + docker run --rm --pull=never et-test sh -c "$du_cmd" + echo "::endgroup::" + echo "::group::et-test: individual files >50M" + find_cmd="find / -xdev -type f -size +50M -exec du -h {} + | sort -h | tail -50" + docker run --rm --pull=never et-test sh -c "$find_cmd" + echo "::endgroup::" + else + echo "et-test image not present (build failed before tagging); skipping in-image size report" + fi # Every cargo knob here is passed INTO the container, not set on the job. # The compile happens inside `docker run`, so a job-level env would never reach cargo. @@ -170,9 +188,10 @@ jobs: env: LANE_ARGS: ${{ matrix.base == 'opensuse/leap:15.6' && '-e CARGO_BUILD_JOBS=2' || '' }} run: | - # $LANE_ARGS is a word-split flag list by design; do not quote it. + args="--rm --pull=never -e CARGO_INCREMENTAL=0 -e CARGO_PROFILE_DEV_DEBUG=0 $LANE_ARGS" + # $args is a word-split flag list by design; do not quote it. # shellcheck disable=SC2086 - docker run --rm -e CARGO_INCREMENTAL=0 -e CARGO_PROFILE_DEV_DEBUG=0 $LANE_ARGS et-test mise run cargo-test + docker run $args et-test mise run cargo-test - name: Run mise check - run: docker run --rm et-check + run: docker run --rm --pull=never et-check diff --git a/.github/workflows/docker-windows.yaml b/.github/workflows/docker-windows.yaml index 777365a..04b694c 100644 --- a/.github/workflows/docker-windows.yaml +++ b/.github/workflows/docker-windows.yaml @@ -128,14 +128,18 @@ jobs: env: IMAGE_TAG: ${{ steps.dockerfile.outputs.tag }} run: | + # `--pull=never` on every run of a locally-built tag. + # A bare name is also a valid Docker Hub reference, so if the local image were ever missing, docker + # would try to pull (and run) whatever squats that name upstream instead of failing with a clear + # "No such image". echo "::group::which rustpython" - docker run --rm "$IMAGE_TAG" cmd /c "mise where http:et-rp & where rustpython" + docker run --rm --pull=never "$IMAGE_TAG" cmd /c "mise where http:et-rp & where rustpython" echo "::endgroup::" echo "::group::rustpython --version" - docker run --rm "$IMAGE_TAG" cmd /c "mise exec -- rustpython --version" + docker run --rm --pull=never "$IMAGE_TAG" cmd /c "mise exec -- rustpython --version" echo "::endgroup::" echo "::group::rustpython -c hello" - docker run --rm "$IMAGE_TAG" cmd /c "mise exec -- rustpython -c \"print('et-rp ok')\"" + docker run --rm --pull=never "$IMAGE_TAG" cmd /c "mise exec -- rustpython -c \"print('et-rp ok')\"" echo "::endgroup::" - name: Build stage test @@ -159,4 +163,4 @@ jobs: # `There is not enough space on the disk. (os error 112)` even after the disk-free step left 43 GB. - name: Run cargo-test if: matrix.base == 'servercore' - run: docker run --rm -e CARGO_INCREMENTAL=0 et-windows-test mise run cargo-test + run: docker run --rm --pull=never -e CARGO_INCREMENTAL=0 et-windows-test mise run cargo-test diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0f9ff9d..2b52326 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,6 +35,11 @@ env: jobs: default: + # Read-only pull of the maintainer-published ghcr.io mise-tools store (install-mise-tools restore step). + # Never write: the stores are published only from a maintainer's machine via `mise run push-mise-tools`. + permissions: + contents: read + packages: read runs-on: ${{ matrix.os }} timeout-minutes: ${{ matrix.timeout }} # Override the workflow-level `shell: bash` default for Windows runs. @@ -152,6 +157,11 @@ jobs: # `mingw` links that msvc archive into an x86_64-pc-windows-gnu binary with winlibs GCC. Separate from # the `default` job because they override the gnullvm default target rather than exercising it. override: + # Read-only pull of the maintainer-published ghcr.io mise-tools store (install-mise-tools restore step). + # Never write: the stores are published only from a maintainer's machine via `mise run push-mise-tools`. + permissions: + contents: read + packages: read runs-on: windows-latest timeout-minutes: 120 strategy: diff --git a/.mise/config.maint.toml b/.mise/config.maint.toml index 6d7d184..4f096ec 100644 --- a/.mise/config.maint.toml +++ b/.mise/config.maint.toml @@ -508,11 +508,116 @@ step rustpython -m cowsay -t "rustpython lives via pip-only path" """ shell = "bash -euo pipefail -c" +# Publish this platform's mise tool tree to its per-platform GHCR store (maintainer-only). +# The stores under ghcr.io/edge-toolkit/core/mise-tools/- feed CI's install-mise-tools restore +# step and the pull-mise-tools task, and are written ONLY from a maintainer's machine -- no workflow holds +# packages:write, so a compromised workflow cannot poison what every job then executes. `mise oci build` +# emits one content-addressed layer per tool; crane does the upload, skipping blobs the registry already +# holds, so re-publishing after a config bump uploads just the changed tools. Each store carries +# host-native binaries: run this on a machine of the platform being published. Needs a one-time +# `docker login ghcr.io` (or `crane auth login ghcr.io`) with a packages:write PAT, and installs the full +# language set first so the image matches what CI expects. +# +# crane pushes the layout rather than `mise oci push` because mise 2026.8.0's built-in registry client +# trips GHCR's chunked blob-upload range rules on large layers and dies mid-push with +# +# mise ERROR uploading blob sha256:0f89655a801ea25e9d3c000287ca276eb8e2b2f890ec3e393e2517facfd7cb9a +# mise ERROR HTTP status client error (416 Range Not Satisfiable) for url +# (https://ghcr.io/v2/edge-toolkit/core/mise-tools/macos-arm64/blobs/upload/3.4effcd8a-...) +# +# (observed locally on macos-arm64, mise 2026.8.0). Revisit once a mise release fixes the chunked upload. +[tasks.push-mise-tools] +description = "Publish this platform's mise tool tree to its per-platform GHCR store (maintainer-only)" +run = """ +detected=$(coreutils uname -sm) +case "$detected" in + Darwin*arm64) plat=macos-arm64 ;; + Darwin*x86_64) plat=macos-x64 ;; + Linux*aarch64 | Linux*arm64) plat=linux-arm64 ;; + Linux*x86_64) plat=linux-x64 ;; + Windows*) plat=windows-x64 ;; + *) echo "unrecognised platform '$detected'" >&2 && exit 1 ;; +esac +ref="ghcr.io/edge-toolkit/core/mise-tools/${plat}:latest" +out="target/scratch/mise-oci-${plat}" +coreutils rm -rf "$out" +MISE_ENV="$ALL_LANGS" mise install +MISE_ENV="$ALL_LANGS" MISE_EXPERIMENTAL=1 mise oci build -o "$out" +crane push "$out" "$ref" +""" +shell = "bash -euo pipefail -c" + +# Publish the linux-x64 mise tool tree store from any host via docker (maintainer-only). +# Lets a maintainer on a macOS laptop refresh the store CI's linux lanes restore from. Reuses the +# Dockerfile's `build` stage -- the same canonical linux tool install CI's docker images use, with docker's +# layer cache making repeats cheap -- then runs `mise oci build` inside the container onto a bind mount and +# pushes the resulting OCI layout from the HOST with crane. Pushing host-side means registry credentials +# never enter the container: the one-time `docker login ghcr.io` (keychain-backed on macOS) covers it. The +# build secret lifts mise's anonymous GitHub rate limit; `gh auth token` supplies it when GITHUB_TOKEN +# isn't already set. On arm64 hosts the amd64 build runs emulated -- the first build is slow (the toolchain +# install runs under qemu/Rosetta); later runs reuse docker layers. +[tasks.push-mise-tools-linux-x64] +description = "Publish the linux-x64 mise tools store via docker from any host (maintainer-only)" +run = """ +ref="ghcr.io/edge-toolkit/core/mise-tools/linux-x64:latest" +out="target/scratch/mise-oci-linux-x64" +GITHUB_TOKEN="${GITHUB_TOKEN:-$(gh auth token)}" +export GITHUB_TOKEN +coreutils rm -rf "$out" +coreutils mkdir -p "$out" +# Bare --build-arg forwards $BASE_IMAGE when set, falling back to the Dockerfile default otherwise. +# (E.g. BASE_IMAGE=ubuntu:26.04 to test another base.) Keep the default: the store's consumers are the +# hosted CI runners on ubuntu 24.04, and tools compiled during install (the go: backend build, any cargo: +# fallback) link the build container's glibc -- a newer base risks GLIBC_2.xx-not-found breakage in the +# restored tree. +args="--platform linux/amd64 --target build --secret id=gh_token,env=GITHUB_TOKEN --build-arg BASE_IMAGE" +# $args is a word-split flag list by design; do not quote it. +# shellcheck disable=SC2086 +DOCKER_BUILDKIT=1 docker build $args -t et-mise-linux . +mnt="$PWD/$out:/oci-out" +docker run --rm --platform linux/amd64 -e MISE_EXPERIMENTAL=1 -v "$mnt" et-mise-linux mise oci build -o /oci-out +crane push "$out" "$ref" +""" +shell = "bash -euo pipefail -c" + +# Verify every mise-tools GHCR store is public, so unauthenticated users can pull-mise-tools. +# GHCR has NO API to change package visibility -- the packages REST surface is read/delete/restore only, +# and the web UI's toggle posts a session-cookie form -- so flipping a store public is a one-time manual +# click. This task automates everything around that click: it lists the org's container packages, prints +# each store's visibility, and fails with the exact settings page URL for any still-private store (flip it +# under Danger Zone -> Change visibility -> Public). Visibility survives later pushes, so each store needs +# this exactly once, right after its first `push-mise-tools*` publish. The listing call needs gh auth with +# the read:packages scope -- grant it once with `gh auth refresh -s read:packages`. +[tasks.check-mise-tools-visibility] +description = "Verify every mise-tools GHCR store is public; print settings URLs for any still private" +run = """ +json=$(gh api "/orgs/edge-toolkit/packages?package_type=container" --paginate) +filter='.[] | select(.name | startswith("core/mise-tools/"))' +report='.name + ": " + .visibility' +still_private='select(.visibility != "public") | .name | @uri' +echo "$json" | jaq -r "$filter | $report" +if [ -z "$(echo "$json" | jaq -r "$filter | .name")" ]; then + echo "no mise-tools packages exist yet; publish one with push-mise-tools first" >&2 + exit 1 +fi +private=$(echo "$json" | jaq -r "$filter | $still_private") +if [ -z "$private" ]; then + echo "all mise-tools stores are public" + exit 0 +fi +echo "still private -- make each public at its settings page (Danger Zone -> Change visibility):" >&2 +for p in $private; do + echo " https://github.com/orgs/edge-toolkit/packages/container/$p/settings" >&2 +done +exit 1 +""" +shell = "bash -euo pipefail -c" + # --- Bootstrap tasks for upstream-cache releases. -# Idempotent gh release view+create -- safe to re-run. Each release's notes are the upstream project URL and -# its license, nothing else (consumers can read the release body to know where the asset came from and under -# what terms). Each release is marked --prerelease so it stays off the repo's "Latest release" badge -# alongside the rp-v release. +# Idempotent gh release view+create -- safe to re-run. +# Each release's notes are the upstream project URL and its license, nothing else (consumers can read the +# release body to know where the asset came from and under what terms). Each release is marked --prerelease +# so it stays off the repo's "Latest release" badge alongside the rp-v release. [tasks.bootstrap-augeas-release] description = "Create the augeas-v1 GitHub release if it doesn't exist (idempotent)" run = """ diff --git a/.mise/config.toml b/.mise/config.toml index e33d259..3b3cbe0 100644 --- a/.mise/config.toml +++ b/.mise/config.toml @@ -117,6 +117,10 @@ ripgrep = "latest" "github:microsoft/onnxruntime" = "1.22.0" "github:owenlamont/ryl" = { version = "latest", os = ["linux", "macos/arm64", "windows"] } "github:wasm-bindgen/wasm-bindgen" = "0.2.126" +# go-containerregistry's crane fetches the per-platform ghcr.io mise-tools stores. +# Its `crane export` rootfs-flatten is what the pull-mise-tools task and CI's install-mise-tools restore +# step extract tool trees with. +crane = "latest" # Sole consumer right now is the `go:` backend that source-builds jqfmt. # (noperator/jqfmt ships no release assets); add Go users here as they arrive. go = "latest" @@ -1348,6 +1352,41 @@ description = "Build et-cli (prereq for the module-package-json steps)" run = "cargo build -p et-cli" shell = "bash -euo pipefail -c" +# Pre-populate the local mise data dir from this platform's GHCR tool store. +# A maintainer publishes each platform's tool tree to ghcr.io//mise-tools/- with the maint +# env's push-mise-tools task (`mise oci push`, one content-addressed layer per tool); this flattens that +# image with `crane export` straight into the local data dir's installs/, so a fresh `mise install` +# afterwards only fills the gaps and regenerates shims instead of downloading everything. Tools that bake +# absolute paths at install time (e.g. pipx venv shebangs) carry the publishing machine's home dir -- +# reinstall any misbehaving one with `mise install --force `. Private packages need a one-time +# `docker login ghcr.io` (or `crane auth login ghcr.io`). +[tasks.pull-mise-tools] +description = "Pre-populate the local mise data dir from this platform's GHCR tool store" +run = """ +detected=$(coreutils uname -sm) +case "$detected" in + Darwin*arm64) def_plat=macos-arm64 ;; + Darwin*x86_64) def_plat=macos-x64 ;; + Linux*aarch64 | Linux*arm64) def_plat=linux-arm64 ;; + Linux*x86_64) def_plat=linux-x64 ;; + Windows*) def_plat=windows-x64 ;; + *) echo "unrecognised platform '$detected'; pass one explicitly" >&2 && exit 1 ;; +esac +plat="${usage_platform:-$def_plat}" +ref="ghcr.io/edge-toolkit/core/mise-tools/${plat}:latest" +if [ "$plat" = "windows-x64" ]; then + data="$LOCALAPPDATA/mise" +else + data="${MISE_DATA_DIR:-$HOME/.local/share/mise}" +fi +coreutils mkdir -p "$data" +echo "pulling $ref -> $data" +crane export "$ref" - | tar -x -C "$data" --strip-components=1 mise/installs +echo "done; run 'mise install' to fill gaps and regenerate shims" +""" +shell = "bash -euo pipefail -c" +usage = 'arg "[platform]" help="CI platform store to pull (default: the current platform)" default=""' + [tasks.build-modules] # Native `depends` glob over `build-ws-*`. # That matches `build-ws-wasm-agent` (always loaded here, no `-module` suffix -- so the glob never diff --git a/.mise/config.zig.toml b/.mise/config.zig.toml index 066de52..e9715d9 100644 --- a/.mise/config.zig.toml +++ b/.mise/config.zig.toml @@ -87,7 +87,7 @@ git ls-files 'services/ws-web-runner/mingw-shim/*.c' | # clang builtin headers (stddef.h etc.), so point -resource-dir at llvm-mingw's headers -- already installed for # the mingw pass above, and the same set the Windows lane's clang_resource_arg uses. Linux keeps conda-clangxx's. set -- --target=wasm32-unknown-unknown -fwasm-exceptions -mexception-handling -fno-rtti -if [ "$(uname -s)" = "Darwin" ]; then +if [ "$(coreutils uname -s)" = "Darwin" ]; then set -- -resource-dir "$(mise where 'github:mstorsjo/llvm-mingw')/lib/clang/22" "$@" else set -- {{ vars.clang_resource_arg }} "$@" @@ -113,7 +113,7 @@ git ls-files 'services/ws-web-runner/mingw-shim/*.c' | # clang builtin headers (stddef.h etc.), so point -resource-dir at llvm-mingw's headers -- already installed for # the mingw pass above, and the same set the Windows lane's clang_resource_arg uses. Linux keeps conda-clangxx's. set -- --target=wasm32-unknown-unknown -fwasm-exceptions -mexception-handling -fno-rtti -if [ "$(uname -s)" = "Darwin" ]; then +if [ "$(coreutils uname -s)" = "Darwin" ]; then set -- -resource-dir "$(mise where 'github:mstorsjo/llvm-mingw')/lib/clang/22" "$@" else set -- {{ vars.clang_resource_arg }} "$@" From 3616112e9ea1c7f5fbd7d30dde39173de83aabc1 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Thu, 13 Aug 2026 13:21:59 +0800 Subject: [PATCH 2/6] retry macos oci --- .../actions/install-mise-tools/action.yaml | 9 +++--- .mise/config.maint.toml | 7 +++++ .mise/config.toml | 29 +++++++++++++++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/.github/actions/install-mise-tools/action.yaml b/.github/actions/install-mise-tools/action.yaml index e0b61d2..536a42e 100644 --- a/.github/actions/install-mise-tools/action.yaml +++ b/.github/actions/install-mise-tools/action.yaml @@ -56,11 +56,10 @@ runs: GH_TOKEN: ${{ inputs.github-token }} run: | # CI dogfoods the same `pull-mise-tools` task local devs run, not its own copy of the pull logic. - # The repo's `task.run_auto_install = false` setting means running the task does NOT first - # cold-install every missing tool of the loaded envs -- so crane, which the task body needs, is - # installed individually up front. (An explicit install also dodges a `mise x` quirk: it has been - # seen exec-failing on the very call that triggers a tool's install -- "couldn't exec process: No - # such file or directory".) + # The task self-installs its own tools, but the ghcr login below runs BEFORE it, so crane is + # installed up front here. (An explicit install also dodges a `mise x` quirk: it has been seen + # exec-failing on the very call that triggers a tool's install -- "couldn't exec process: No such + # file or directory".) mise install crane # crane's login writes the standard ~/.docker/config.json for the task's pull. # printf-pipe rather than a `<<<` herestring: this step's declared bash has herestrings, but the diff --git a/.mise/config.maint.toml b/.mise/config.maint.toml index 4f096ec..712f1f7 100644 --- a/.mise/config.maint.toml +++ b/.mise/config.maint.toml @@ -529,6 +529,8 @@ shell = "bash -euo pipefail -c" [tasks.push-mise-tools] description = "Publish this platform's mise tool tree to its per-platform GHCR store (maintainer-only)" run = """ +# Self-install the tools this body uses; `task.run_auto_install = false` keeps `mise run` from doing it. +mise install coreutils crane detected=$(coreutils uname -sm) case "$detected" in Darwin*arm64) plat=macos-arm64 ;; @@ -559,6 +561,9 @@ shell = "bash -euo pipefail -c" [tasks.push-mise-tools-linux-x64] description = "Publish the linux-x64 mise tools store via docker from any host (maintainer-only)" run = """ +# Self-install the tools this body uses; `task.run_auto_install = false` keeps `mise run` from doing it. +# (docker itself is a host daemon, not a mise tool.) +mise install coreutils crane gh ref="ghcr.io/edge-toolkit/core/mise-tools/linux-x64:latest" out="target/scratch/mise-oci-linux-x64" GITHUB_TOKEN="${GITHUB_TOKEN:-$(gh auth token)}" @@ -591,6 +596,8 @@ shell = "bash -euo pipefail -c" [tasks.check-mise-tools-visibility] description = "Verify every mise-tools GHCR store is public; print settings URLs for any still private" run = """ +# Self-install the tools this body uses; `task.run_auto_install = false` keeps `mise run` from doing it. +mise install gh 'aqua:01mf02/jaq' json=$(gh api "/orgs/edge-toolkit/packages?package_type=container" --paginate) filter='.[] | select(.name | startswith("core/mise-tools/"))' report='.name + ": " + .visibility' diff --git a/.mise/config.toml b/.mise/config.toml index 3b3cbe0..7a2cf44 100644 --- a/.mise/config.toml +++ b/.mise/config.toml @@ -1363,6 +1363,10 @@ shell = "bash -euo pipefail -c" [tasks.pull-mise-tools] description = "Pre-populate the local mise data dir from this platform's GHCR tool store" run = """ +# Self-install the tools this body uses. +# `task.run_auto_install = false` means running a task installs nothing, and this task exists precisely +# for checkouts that haven't run a full `mise install` yet. +mise install coreutils crane detected=$(coreutils uname -sm) case "$detected" in Darwin*arm64) def_plat=macos-arm64 ;; @@ -1379,9 +1383,28 @@ if [ "$plat" = "windows-x64" ]; then else data="${MISE_DATA_DIR:-$HOME/.local/share/mise}" fi -coreutils mkdir -p "$data" -echo "pulling $ref -> $data" -crane export "$ref" - | tar -x -C "$data" --strip-components=1 mise/installs +# Extract to scratch, then move over only the tool versions not already installed. +# Extracting straight onto a populated data dir fails -- tar refuses to write through existing symlinks +# ("Cannot extract through symlink installs/http-augeas//bin", observed locally) -- and +# already-installed tools should win over the store's copies anyway. +tmp="target/scratch/pull-mise-tools" +coreutils rm -rf "$tmp" +coreutils mkdir -p "$tmp" "$data/installs" +echo "pulling $ref" +crane export "$ref" - | tar -x -C "$tmp" --strip-components=1 mise/installs +restored=0 +for d in "$tmp"/installs/*/*/; do + [ -d "$d" ] || continue + rel="${d#"$tmp"/installs/}" + dest="$data/installs/$rel" + if [ ! -e "$dest" ]; then + coreutils mkdir -p "$(coreutils dirname "$dest")" + coreutils mv "$d" "$dest" + restored=$((restored + 1)) + fi +done +coreutils rm -rf "$tmp" +echo "restored $restored tool versions into $data/installs" echo "done; run 'mise install' to fill gaps and regenerate shims" """ shell = "bash -euo pipefail -c" From 0b2b1e0d97b4f33750f8f6f3d56564d34c50c6b5 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Thu, 13 Aug 2026 14:13:50 +0800 Subject: [PATCH 3/6] try macos again --- .mise/config.maint.toml | 11 ++++------- .mise/config.toml | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.mise/config.maint.toml b/.mise/config.maint.toml index 712f1f7..5706453 100644 --- a/.mise/config.maint.toml +++ b/.mise/config.maint.toml @@ -527,10 +527,9 @@ shell = "bash -euo pipefail -c" # # (observed locally on macos-arm64, mise 2026.8.0). Revisit once a mise release fixes the chunked upload. [tasks.push-mise-tools] +depends = ["_setup-store-tools"] description = "Publish this platform's mise tool tree to its per-platform GHCR store (maintainer-only)" run = """ -# Self-install the tools this body uses; `task.run_auto_install = false` keeps `mise run` from doing it. -mise install coreutils crane detected=$(coreutils uname -sm) case "$detected" in Darwin*arm64) plat=macos-arm64 ;; @@ -559,11 +558,10 @@ shell = "bash -euo pipefail -c" # isn't already set. On arm64 hosts the amd64 build runs emulated -- the first build is slow (the toolchain # install runs under qemu/Rosetta); later runs reuse docker layers. [tasks.push-mise-tools-linux-x64] +depends = ["_setup-store-tools"] description = "Publish the linux-x64 mise tools store via docker from any host (maintainer-only)" +# docker itself is a host daemon, not a mise tool; everything else comes from the depends helper. run = """ -# Self-install the tools this body uses; `task.run_auto_install = false` keeps `mise run` from doing it. -# (docker itself is a host daemon, not a mise tool.) -mise install coreutils crane gh ref="ghcr.io/edge-toolkit/core/mise-tools/linux-x64:latest" out="target/scratch/mise-oci-linux-x64" GITHUB_TOKEN="${GITHUB_TOKEN:-$(gh auth token)}" @@ -594,10 +592,9 @@ shell = "bash -euo pipefail -c" # this exactly once, right after its first `push-mise-tools*` publish. The listing call needs gh auth with # the read:packages scope -- grant it once with `gh auth refresh -s read:packages`. [tasks.check-mise-tools-visibility] +depends = ["_setup-store-tools"] description = "Verify every mise-tools GHCR store is public; print settings URLs for any still private" run = """ -# Self-install the tools this body uses; `task.run_auto_install = false` keeps `mise run` from doing it. -mise install gh 'aqua:01mf02/jaq' json=$(gh api "/orgs/edge-toolkit/packages?package_type=container" --paginate) filter='.[] | select(.name | startswith("core/mise-tools/"))' report='.name + ": " + .visibility' diff --git a/.mise/config.toml b/.mise/config.toml index 7a2cf44..55eed47 100644 --- a/.mise/config.toml +++ b/.mise/config.toml @@ -1352,6 +1352,18 @@ description = "Build et-cli (prereq for the module-package-json steps)" run = "cargo build -p et-cli" shell = "bash -euo pipefail -c" +# Install the tools the GHCR-store tasks call, before their PATH is composed. +# A task's PATH is fixed when the task starts, so a `mise install` INSIDE a body leaves the just-installed +# bare names unresolvable ("bash: line 4: coreutils: command not found" on a cold runner, observed at +# https://github.com/edge-toolkit/core/actions/runs/31670112517/job/94352839713 on commit +# d8ad8cb99cf58a1e505cdbebc5b94f9893532b4f). A `depends` task runs before that composition, so the +# store tasks (pull-mise-tools here, the push/visibility maint tasks) get working tools on any checkout +# that has never run a full `mise install`. gh is only needed by the maint tasks, but one shared helper +# beats three near-copies. +[tasks._setup-store-tools] +description = "Install the tools the mise-tools store tasks depend on (cold-checkout helper)" +run = "mise install coreutils crane gh 'aqua:01mf02/jaq'" + # Pre-populate the local mise data dir from this platform's GHCR tool store. # A maintainer publishes each platform's tool tree to ghcr.io//mise-tools/- with the maint # env's push-mise-tools task (`mise oci push`, one content-addressed layer per tool); this flattens that @@ -1361,12 +1373,9 @@ shell = "bash -euo pipefail -c" # reinstall any misbehaving one with `mise install --force `. Private packages need a one-time # `docker login ghcr.io` (or `crane auth login ghcr.io`). [tasks.pull-mise-tools] +depends = ["_setup-store-tools"] description = "Pre-populate the local mise data dir from this platform's GHCR tool store" run = """ -# Self-install the tools this body uses. -# `task.run_auto_install = false` means running a task installs nothing, and this task exists precisely -# for checkouts that haven't run a full `mise install` yet. -mise install coreutils crane detected=$(coreutils uname -sm) case "$detected" in Darwin*arm64) def_plat=macos-arm64 ;; @@ -1383,15 +1392,27 @@ if [ "$plat" = "windows-x64" ]; then else data="${MISE_DATA_DIR:-$HOME/.local/share/mise}" fi -# Extract to scratch, then move over only the tool versions not already installed. -# Extracting straight onto a populated data dir fails -- tar refuses to write through existing symlinks -# ("Cannot extract through symlink installs/http-augeas//bin", observed locally) -- and -# already-installed tools should win over the store's copies anyway. +# Pull the image as an OCI layout, then extract its layers locally into scratch. +# One `crane export` stream of the whole multi-GB rootfs is fragile -- GHCR killed it mid-transfer with +# +# Error: http2: server sent GOAWAY and closed the connection; ... debug="idle timeout" +# ...: Truncated tar archive +# +# (observed locally). `crane pull` instead fetches each digest-verified layer as its own retried request. +# The layers land in scratch rather than straight onto the data dir, both to survive a mid-pull failure +# and because tar refuses to write through existing symlinks on a populated data dir ("Cannot extract +# through symlink installs/http-augeas//bin", observed locally). tmp="target/scratch/pull-mise-tools" coreutils rm -rf "$tmp" coreutils mkdir -p "$tmp" "$data/installs" echo "pulling $ref" -crane export "$ref" - | tar -x -C "$tmp" --strip-components=1 mise/installs +crane pull --format=oci "$ref" "$tmp/oci" +manifest=$(jaq -r '.manifests[0].digest | ltrimstr("sha256:")' "$tmp/oci/index.json") +layers=$(jaq -r '.layers[].digest | ltrimstr("sha256:")' "$tmp/oci/blobs/sha256/$manifest") +# Layers that carry no mise/installs entries (the base image's) make tar exit non-zero; that miss is fine. +for layer in $layers; do + tar -xzf "$tmp/oci/blobs/sha256/$layer" -C "$tmp" --strip-components=1 mise/installs 2>/dev/null || true +done restored=0 for d in "$tmp"/installs/*/*/; do [ -d "$d" ] || continue From 0dd8cb009e2b81f9013d7a647092adf383391284 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Fri, 14 Aug 2026 13:42:18 +0800 Subject: [PATCH 4/6] fix token; bump WASI --- .../actions/install-mise-tools/action.yaml | 4 +- .mise/mise.js.lock | 2 + .mise/mise.lock | 29 +++++ .mise/mise.r.lock | 1 + .mise/mise.zig.lock | 111 ++++++++++++++++++ config/typos.toml | 5 +- .../wit/deps/wasi-clocks/monotonic-clock.wit | 9 +- .../specs/wit/deps/wasi-clocks/timezone.wit | 2 +- .../specs/wit/deps/wasi-clocks/wall-clock.wit | 2 +- .../specs/wit/deps/wasi-clocks/world.wit | 2 +- generated/specs/wit/deps/wasi-io/error.wit | 2 +- generated/specs/wit/deps/wasi-io/poll.wit | 2 +- generated/specs/wit/deps/wasi-io/streams.wit | 52 ++------ generated/specs/wit/deps/wasi-io/world.wit | 2 +- generated/specs/wit/world.wit | 6 +- utilities/int-gen/src/wit/upstream.rs | 4 +- 16 files changed, 177 insertions(+), 58 deletions(-) diff --git a/.github/actions/install-mise-tools/action.yaml b/.github/actions/install-mise-tools/action.yaml index 536a42e..a40b23e 100644 --- a/.github/actions/install-mise-tools/action.yaml +++ b/.github/actions/install-mise-tools/action.yaml @@ -53,7 +53,7 @@ runs: - name: Restore mise tools from GHCR shell: bash --noprofile --norc -euo pipefail {0} env: - GH_TOKEN: ${{ inputs.github-token }} + GITHUB_TOKEN: ${{ inputs.github-token }} run: | # CI dogfoods the same `pull-mise-tools` task local devs run, not its own copy of the pull logic. # The task self-installs its own tools, but the ghcr login below runs BEFORE it, so crane is @@ -64,7 +64,7 @@ runs: # crane's login writes the standard ~/.docker/config.json for the task's pull. # printf-pipe rather than a `<<<` herestring: this step's declared bash has herestrings, but the # repo's Windows task shell (busybox ash) does not, and this line is the kind that gets copied. - printf %s "$GH_TOKEN" | mise x crane -- crane auth login ghcr.io -u "$GITHUB_ACTOR" --password-stdin + printf %s "$GITHUB_TOKEN" | mise x crane -- crane auth login ghcr.io -u "$GITHUB_ACTOR" --password-stdin if mise run pull-mise-tools; then echo "restored this platform's mise-tools store" else diff --git a/.mise/mise.js.lock b/.mise/mise.js.lock index 4dc8cd4..ccbed4d 100644 --- a/.mise/mise.js.lock +++ b/.mise/mise.js.lock @@ -11,6 +11,7 @@ rename_exe = "oxfmt" url = "https://github.com/oxc-project/oxc/releases/download/apps_v1.69.0/oxfmt-aarch64-unknown-linux-gnu.tar.gz" [tools."http:oxfmt"."platforms.linux-x64"] +checksum = "blake3:81f1adca5230c938968d88e06bc9d9ae6d9d34ee0c849d1e4b43d07562605ec5" url = "https://github.com/oxc-project/oxc/releases/download/apps_v1.69.0/oxfmt-x86_64-unknown-linux-gnu.tar.gz" [tools."http:oxfmt"."platforms.macos-arm64"] @@ -33,6 +34,7 @@ rename_exe = "oxlint" url = "https://github.com/oxc-project/oxc/releases/download/apps_v1.73.0/oxlint-aarch64-unknown-linux-gnu.tar.gz" [tools."http:oxlint"."platforms.linux-x64"] +checksum = "blake3:974e8e6b96e5e21882977efdf829ae4043e5424351f27a245ce22da93670fd94" url = "https://github.com/oxc-project/oxc/releases/download/apps_v1.73.0/oxlint-x86_64-unknown-linux-gnu.tar.gz" [tools."http:oxlint"."platforms.macos-arm64"] diff --git a/.mise/mise.lock b/.mise/mise.lock index 6f5ea38..21461d3 100644 --- a/.mise/mise.lock +++ b/.mise/mise.lock @@ -509,6 +509,35 @@ checksum = "sha256:f223058a51e8a2a4b92d9f1a1b2954f7a02b6d0f42e4a7e0413fcdb93be2e url = "https://github.com/uutils/coreutils/releases/download/0.6.0/coreutils-0.6.0-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/uutils/coreutils/releases/assets/349509944" +[[tools.crane]] +version = "0.21.9" +backend = "aqua:google/go-containerregistry" + +[tools.crane."platforms.linux-arm64"] +checksum = "sha256:1f4c647b7bb260ab5435661df5b526cf59950ebf95201790db7183ac189cbcbd" +url = "https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/google/go-containerregistry/releases/assets/503108017" + +[tools.crane."platforms.linux-x64"] +checksum = "sha256:5c16d8ddb971cb1d5e6ed8b1e743da8224414eeba2c2762d8f1a61b2f095699e" +url = "https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Linux_x86_64.tar.gz" +url_api = "https://api.github.com/repos/google/go-containerregistry/releases/assets/503108053" + +[tools.crane."platforms.macos-arm64"] +checksum = "sha256:11cc3640e53473eb0d8c501068573e52a259f4d82177e6dd11b7bacb8955459e" +url = "https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/google/go-containerregistry/releases/assets/503108022" + +[tools.crane."platforms.macos-x64"] +checksum = "sha256:f31075b3375f79b406a600e090d5c4778b3c6598a01c817dc7898c05c7c00a56" +url = "https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Darwin_x86_64.tar.gz" +url_api = "https://api.github.com/repos/google/go-containerregistry/releases/assets/503108009" + +[tools.crane."platforms.windows-x64"] +checksum = "sha256:2309f41ce9a31b6096fd590a57b00b0fd1d6dc41d315dc820795f64be070fe03" +url = "https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Windows_x86_64.tar.gz" +url_api = "https://api.github.com/repos/google/go-containerregistry/releases/assets/503108006" + [[tools.dprint]] version = "0.54.0" backend = "aqua:dprint/dprint" diff --git a/.mise/mise.r.lock b/.mise/mise.r.lock index 0658739..0560857 100644 --- a/.mise/mise.r.lock +++ b/.mise/mise.r.lock @@ -8,6 +8,7 @@ backend = "http:webr" url = "https://github.com/r-wasm/webr/releases/download/v0.6.0/webr-0.6.0.tar.gz" [tools."http:webr"."platforms.linux-x64"] +checksum = "blake3:764c56176d67d64b858325fcc245d653ef5993dc01df3e7f8929448439d9785d" url = "https://github.com/r-wasm/webr/releases/download/v0.6.0/webr-0.6.0.tar.gz" [tools."http:webr"."platforms.macos-arm64"] diff --git a/.mise/mise.zig.lock b/.mise/mise.zig.lock index 43fc937..b3888b2 100644 --- a/.mise/mise.zig.lock +++ b/.mise/mise.zig.lock @@ -64,6 +64,74 @@ checksum = "sha256:76efa6cc9d7e6f5ee3bbca0939f64054af2bdfb3c3632531f8e633cf6c2ea url = "https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda" checksum = "sha256:427fd14bcb3b8659796fecc682716617409350fb5a98e5b7b47558a10d1a2fc7" +[conda-packages.linux-x64."_openmp_mutex-4.5-20_gnu"] +url = "https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda" +checksum = "sha256:1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9" + +[conda-packages.linux-x64."clang-format-22-22.1.8-default_h08c5240_6"] +url = "https://conda.anaconda.org/conda-forge/linux-64/clang-format-22-22.1.8-default_h08c5240_6.conda" +checksum = "sha256:8e0fb4bf20bc89b5de16357e73a8d3344db814aafcab354aecf968571e62fb21" + +[conda-packages.linux-x64."clang-format-22.1.8-default_h36682a0_6"] +url = "https://conda.anaconda.org/conda-forge/linux-64/clang-format-22.1.8-default_h36682a0_6.conda" +checksum = "sha256:03a3c632e594375b1416c0baf9131596aa4b11e80a8f557f69162fd1c7aed309" + +[conda-packages.linux-x64."clang-scan-deps-22.1.8-default_h08c5240_6"] +url = "https://conda.anaconda.org/conda-forge/linux-64/clang-scan-deps-22.1.8-default_h08c5240_6.conda" +checksum = "sha256:f41e133972fdd5ae6d72316b629c4c8824b40149afe584df09566689c4fe5b52" + +[conda-packages.linux-x64."icu-78.3-py310h44b86e0_2"] +url = "https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda" +checksum = "sha256:9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432" + +[conda-packages.linux-x64."libclang-cpp22.1-22.1.8-default_h08c5240_6"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.8-default_h08c5240_6.conda" +checksum = "sha256:fc2981a8e45dd232ae88cf2580f7e75c258f4dccdc96f879b68ef0a9b45c9a2b" + +[conda-packages.linux-x64."libclang13-22.1.8-default_hd70ba2e_6"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.8-default_hd70ba2e_6.conda" +checksum = "sha256:8475136d3be6c2d16bdb801dcaf8769cb2ba372cad239c89ebd6a58912e14465" + +[conda-packages.linux-x64."libgcc-16.1.0-ha9f2e26_1"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda" +checksum = "sha256:d5cb8475131c31680f8fd30512c418f373064e272e452063276a8fb14c9fa42f" + +[conda-packages.linux-x64."libgomp-16.1.0-he0feb66_1"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda" +checksum = "sha256:62cb599ad0539d99386515326d9d5e8f51f75a60c69c2131b21df76edf35bd89" + +[conda-packages.linux-x64."libiconv-1.18-h3b78370_2"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda" +checksum = "sha256:c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f" + +[conda-packages.linux-x64."libllvm22-22.1.8-hf7376ad_1"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.8-hf7376ad_1.conda" +checksum = "sha256:e9b5f301d6b001a9b8ce782157f56b75c92c4fbc9eba95dc6345c1139251d13b" + +[conda-packages.linux-x64."liblzma-5.8.3-hb03c661_1"] +url = "https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda" +checksum = "sha256:9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab" + +[conda-packages.linux-x64."libstdcxx-16.1.0-h934c35e_1"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda" +checksum = "sha256:79721dd08aeb0ab9e773f1f9ef41cf4e6c17477e3d72319147619045bce05a09" + +[conda-packages.linux-x64."libxml2-16-2.15.3-hca6bf5a_0"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda" +checksum = "sha256:3d44f737c5ae52d5af32682cc1530df433f401f8e58a7533926536244127572a" + +[conda-packages.linux-x64."libxml2-2.15.3-h49c6c72_0"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda" +checksum = "sha256:3bc5551720c58591f6ea1146f7d1539c734ed1c40e7b9f5cb8cb7e900c509aba" + +[conda-packages.linux-x64."libzlib-1.3.2-h25fd6f3_3"] +url = "https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda" +checksum = "sha256:eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736" + +[conda-packages.linux-x64."zstd-1.5.7-hb78ec9c_7"] +url = "https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda" +checksum = "sha256:47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60" + [conda-packages.macos-arm64."clang-format-22-22.1.8-default_hc257da1_6"] url = "https://conda.anaconda.org/conda-forge/osx-arm64/clang-format-22-22.1.8-default_hc257da1_6.conda" checksum = "sha256:6006eca9bf44cf05e7de18fe9468a559b0ff90c858004ba726db82f0f728d112" @@ -246,6 +314,26 @@ conda_deps = [ "liblzma-5.8.3-he30d5cf_1", ] +[tools."conda:clang-format"."platforms.linux-x64"] +checksum = "sha256:03a3c632e594375b1416c0baf9131596aa4b11e80a8f557f69162fd1c7aed309" +url = "https://conda.anaconda.org/conda-forge/linux-64/clang-format-22.1.8-default_h36682a0_6.conda" +conda_deps = [ + "clang-format-22-22.1.8-default_h08c5240_6", + "libclang-cpp22.1-22.1.8-default_h08c5240_6", + "libzlib-1.3.2-h25fd6f3_3", + "libstdcxx-16.1.0-h934c35e_1", + "libgcc-16.1.0-ha9f2e26_1", + "zstd-1.5.7-hb78ec9c_7", + "libxml2-2.15.3-h49c6c72_0", + "libxml2-16-2.15.3-hca6bf5a_0", + "libllvm22-22.1.8-hf7376ad_1", + "_openmp_mutex-4.5-20_gnu", + "libgomp-16.1.0-he0feb66_1", + "libiconv-1.18-h3b78370_2", + "liblzma-5.8.3-hb03c661_1", + "icu-78.3-py310h44b86e0_2", +] + [tools."conda:clang-format"."platforms.macos-arm64"] checksum = "sha256:39b0943dcd17275b153c87c067b54c0f8645be4839321fcd539358eb175d5195" url = "https://conda.anaconda.org/conda-forge/osx-arm64/clang-format-22.1.8-default_h1fde8bb_6.conda" @@ -324,6 +412,29 @@ conda_deps = [ "liblzma-5.8.3-he30d5cf_1", ] +[tools."conda:clang-tools"."platforms.linux-x64"] +checksum = "sha256:ce6381b8a5d144d38d092c9cd9bfcf2fc303b9bf5bb45ddf3691767a1b3cdceb" +url = "https://conda.anaconda.org/conda-forge/linux-64/clang-tools-22.1.8-default_h1af49a9_6.conda" +conda_deps = [ + "clang-scan-deps-22.1.8-default_h08c5240_6", + "clang-format-22.1.8-default_h36682a0_6", + "clang-format-22-22.1.8-default_h08c5240_6", + "libzlib-1.3.2-h25fd6f3_3", + "libllvm22-22.1.8-hf7376ad_1", + "libclang13-22.1.8-default_hd70ba2e_6", + "libclang-cpp22.1-22.1.8-default_h08c5240_6", + "libstdcxx-16.1.0-h934c35e_1", + "libgcc-16.1.0-ha9f2e26_1", + "zstd-1.5.7-hb78ec9c_7", + "libxml2-2.15.3-h49c6c72_0", + "libxml2-16-2.15.3-hca6bf5a_0", + "_openmp_mutex-4.5-20_gnu", + "libgomp-16.1.0-he0feb66_1", + "libiconv-1.18-h3b78370_2", + "liblzma-5.8.3-hb03c661_1", + "icu-78.3-py310h44b86e0_2", +] + [tools."conda:clang-tools"."platforms.macos-arm64"] checksum = "sha256:a27f988b3319ec1b62c3cfbe0538936e5feb061ac95154147fb60d80b03ebb86" url = "https://conda.anaconda.org/conda-forge/osx-arm64/clang-tools-22.1.8-default_ha2fb705_6.conda" diff --git a/config/typos.toml b/config/typos.toml index d7829be..f4ddfd9 100644 --- a/config/typos.toml +++ b/config/typos.toml @@ -10,4 +10,7 @@ extend-exclude = ["data/"] # `lod-min-clamp` / `lod-max-clamp` are WebGPU sampler fields (level-of-detail) in the vendored # wasi:webgpu WIT, not misspellings of "load". Scoped to those two identifiers so bare "lod" is still # flagged everywhere else. -extend-ignore-re = ["MIS Quarterly", "lod-(min|max)-clamp"] +# "Functionality is equivelant to" is upstream wasi-io 0.2.8's own typo in a vendored WIT doc comment; +# the file is fetched verbatim by fetch-wit-deps, so it cannot be corrected here. Anchored to the whole +# phrase so the misspelling is still caught in prose we write. +extend-ignore-re = ["Functionality is equivelant to", "MIS Quarterly", "lod-(min|max)-clamp"] diff --git a/generated/specs/wit/deps/wasi-clocks/monotonic-clock.wit b/generated/specs/wit/deps/wasi-clocks/monotonic-clock.wit index f3bc839..e60f366 100644 --- a/generated/specs/wit/deps/wasi-clocks/monotonic-clock.wit +++ b/generated/specs/wit/deps/wasi-clocks/monotonic-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.6; +package wasi:clocks@0.2.8; /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -10,7 +10,7 @@ package wasi:clocks@0.2.6; @since(version = 0.2.0) interface monotonic-clock { @since(version = 0.2.0) - use wasi:io/poll@0.2.6.{pollable}; + use wasi:io/poll@0.2.8.{pollable}; /// An instant in time, in nanoseconds. An instant is relative to an /// unspecified initial value, and can only be compared to instances from @@ -26,6 +26,11 @@ interface monotonic-clock { /// /// The clock is monotonic, therefore calling this function repeatedly will /// produce a sequence of non-decreasing values. + /// + /// For completeness, this function traps if it's not possible to represent + /// the value of the clock in an `instant`. Consequently, implementations + /// should ensure that the starting time is low enough to avoid the + /// possibility of overflow in practice. @since(version = 0.2.0) now: func() -> instant; diff --git a/generated/specs/wit/deps/wasi-clocks/timezone.wit b/generated/specs/wit/deps/wasi-clocks/timezone.wit index ca98ad1..534814a 100644 --- a/generated/specs/wit/deps/wasi-clocks/timezone.wit +++ b/generated/specs/wit/deps/wasi-clocks/timezone.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.6; +package wasi:clocks@0.2.8; @unstable(feature = clocks-timezone) interface timezone { diff --git a/generated/specs/wit/deps/wasi-clocks/wall-clock.wit b/generated/specs/wit/deps/wasi-clocks/wall-clock.wit index 76636a0..3386c80 100644 --- a/generated/specs/wit/deps/wasi-clocks/wall-clock.wit +++ b/generated/specs/wit/deps/wasi-clocks/wall-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.6; +package wasi:clocks@0.2.8; /// WASI Wall Clock is a clock API intended to let users query the current /// time. The name "wall" makes an analogy to a "clock on the wall", which /// is not necessarily monotonic as it may be reset. diff --git a/generated/specs/wit/deps/wasi-clocks/world.wit b/generated/specs/wit/deps/wasi-clocks/world.wit index 5c53c51..1655ca8 100644 --- a/generated/specs/wit/deps/wasi-clocks/world.wit +++ b/generated/specs/wit/deps/wasi-clocks/world.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.6; +package wasi:clocks@0.2.8; @since(version = 0.2.0) world imports { diff --git a/generated/specs/wit/deps/wasi-io/error.wit b/generated/specs/wit/deps/wasi-io/error.wit index 784f74a..dd5a1af 100644 --- a/generated/specs/wit/deps/wasi-io/error.wit +++ b/generated/specs/wit/deps/wasi-io/error.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.6; +package wasi:io@0.2.8; @since(version = 0.2.0) interface error { diff --git a/generated/specs/wit/deps/wasi-io/poll.wit b/generated/specs/wit/deps/wasi-io/poll.wit index 7f71183..833b381 100644 --- a/generated/specs/wit/deps/wasi-io/poll.wit +++ b/generated/specs/wit/deps/wasi-io/poll.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.6; +package wasi:io@0.2.8; /// A poll API intended to let users wait for I/O events on multiple handles /// at once. diff --git a/generated/specs/wit/deps/wasi-io/streams.wit b/generated/specs/wit/deps/wasi-io/streams.wit index c5da38c..fbb0268 100644 --- a/generated/specs/wit/deps/wasi-io/streams.wit +++ b/generated/specs/wit/deps/wasi-io/streams.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.6; +package wasi:io@0.2.8; /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. @@ -154,27 +154,13 @@ interface streams { /// Perform a write of up to 4096 bytes, and then flush the stream. Block /// until all of these operations are complete, or an error occurs. /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe`, `write`, and `flush`, and is implemented with the - /// following pseudo-code: - /// - /// ```text - /// let pollable = this.subscribe(); - /// while !contents.is_empty() { - /// // Wait for the stream to become writable - /// pollable.block(); - /// let Ok(n) = this.check-write(); // eliding error handling - /// let len = min(n, contents.len()); - /// let (chunk, rest) = contents.split_at(len); - /// this.write(chunk ); // eliding error handling - /// contents = rest; - /// } - /// this.flush(); - /// // Wait for completion of `flush` - /// pollable.block(); - /// // Check for any errors that arose during `flush` - /// let _ = this.check-write(); // eliding error handling - /// ``` + /// Returns success when all of the contents written are successfully + /// flushed to output. If an error occurs at any point before all + /// contents are successfully flushed, that error is returned as soon as + /// possible. If writing and flushing the complete contents causes the + /// stream to become closed, this call should return success, and + /// subsequent calls to check-write or other interfaces should return + /// stream-error::closed. @since(version = 0.2.0) blocking-write-and-flush: func( contents: list @@ -227,26 +213,8 @@ interface streams { /// Block until all of these operations are complete, or an error /// occurs. /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with - /// the following pseudo-code: - /// - /// ```text - /// let pollable = this.subscribe(); - /// while num_zeroes != 0 { - /// // Wait for the stream to become writable - /// pollable.block(); - /// let Ok(n) = this.check-write(); // eliding error handling - /// let len = min(n, num_zeroes); - /// this.write-zeroes(len); // eliding error handling - /// num_zeroes -= len; - /// } - /// this.flush(); - /// // Wait for completion of `flush` - /// pollable.block(); - /// // Check for any errors that arose during `flush` - /// let _ = this.check-write(); // eliding error handling - /// ``` + /// Functionality is equivelant to `blocking-write-and-flush` with + /// contents given as a list of len containing only zeroes. @since(version = 0.2.0) blocking-write-zeroes-and-flush: func( /// The number of zero-bytes to write diff --git a/generated/specs/wit/deps/wasi-io/world.wit b/generated/specs/wit/deps/wasi-io/world.wit index 84c85c0..1cc3fce 100644 --- a/generated/specs/wit/deps/wasi-io/world.wit +++ b/generated/specs/wit/deps/wasi-io/world.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.6; +package wasi:io@0.2.8; @since(version = 0.2.0) world imports { diff --git a/generated/specs/wit/world.wit b/generated/specs/wit/world.wit index 544c386..5f57f39 100644 --- a/generated/specs/wit/world.wit +++ b/generated/specs/wit/world.wit @@ -117,9 +117,9 @@ world runner { // `add_to_linker`. world module { include runner; - import wasi:clocks/wall-clock@0.2.6; - import wasi:clocks/monotonic-clock@0.2.6; - import wasi:io/poll@0.2.6; + import wasi:clocks/wall-clock@0.2.8; + import wasi:clocks/monotonic-clock@0.2.8; + import wasi:io/poll@0.2.8; import wasi:webgpu/webgpu@0.3.0-rc.2; import wasi:nn/tensor@0.2.0-rc-2024-10-28; import wasi:nn/graph@0.2.0-rc-2024-10-28; diff --git a/utilities/int-gen/src/wit/upstream.rs b/utilities/int-gen/src/wit/upstream.rs index e49c31a..56438c1 100644 --- a/utilities/int-gen/src/wit/upstream.rs +++ b/utilities/int-gen/src/wit/upstream.rs @@ -35,7 +35,7 @@ const PACKAGES: &[UpstreamPackage] = &[ UpstreamPackage { local_dir: "wasi-clocks", repo: "wasi-clocks", - git_ref: "v0.2.6", + git_ref: "v0.2.8", files: &[ File { name: "monotonic-clock.wit", @@ -48,7 +48,7 @@ const PACKAGES: &[UpstreamPackage] = &[ UpstreamPackage { local_dir: "wasi-io", repo: "wasi-io", - git_ref: "v0.2.6", + git_ref: "v0.2.8", files: &[ File { name: "error.wit" }, File { name: "poll.wit" }, From 723993e4caaa6dace4a1aeff23164900cc0e68ea Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Fri, 14 Aug 2026 17:08:01 +0800 Subject: [PATCH 5/6] windows fixes --- .mise/config.java.toml | 11 +++++---- .mise/config.kotlin.toml | 6 ++--- .mise/config.maint.toml | 31 +++++++++++++++---------- .mise/config.toml | 50 +++++++++++++++++++++++++--------------- .mise/config.zig.toml | 35 +++++++++++++++++++++++----- .mise/mise.lock | 3 +++ .mise/mise.r.lock | 1 + .mise/mise.windows.lock | 8 +++---- 8 files changed, 97 insertions(+), 48 deletions(-) diff --git a/.mise/config.java.toml b/.mise/config.java.toml index 024c827..bb169cc 100644 --- a/.mise/config.java.toml +++ b/.mise/config.java.toml @@ -47,8 +47,9 @@ MAVEN_ARGS = "--no-transfer-progress" # busybox's own sh applet -- but the Unix script's JAVACMD composition (`JAVACMD="$JAVA_HOME/bin/java"`, no # `.exe`) then fails its `[ -x "$JAVACMD" ]` test because on Windows the file is named `bin/java.exe`. Bare # `mvn.cmd` (and `cmd.exe /c mvn`) both fail in the all-langs MISE_ENV with "'mvn[.cmd]' is not recognized" -- -# the wrapping cmd.exe cannot see maven\bin in its inherited PATH (likely a length/encoding issue introduced -# by the wider tool set; works fine with MISE_ENV=java alone). Use the absolute path to `mvn.cmd` instead: +# the wrapping cmd.exe discards the over-long PATH it inherits, so maven\bin is not there to be found (which +# is also why MISE_ENV=java alone works: its narrower tool set stays under cmd.exe's per-variable PATH limit). +# Use the absolute path to `mvn.cmd` instead: # busybox-w32's spawnve detects `.cmd` and auto-wraps with cmd.exe, which runs the full path directly with no # PATH lookup required. `vars.maven_bin` is defined in config.windows.toml; vars merge across all loaded # configs. @@ -60,17 +61,17 @@ mvn [tasks.build-ws-java-data1-module] description = "Build the java-data1 workflow module" -run = "$MVN package" +run = "\"$MVN\" package" shell = "bash -euo pipefail -c" # Namespaced aggregator picked up by the default config's globbed `check`. # The compile triggers maven-compiler-plugin with -Xlint:all -Werror + Error Prone. [tasks."check:java"] description = "Run Java checks (javac -Xlint:all -Werror, Error Prone)" -run = "$MVN -q compile" +run = "\"$MVN\" -q compile" shell = "bash -euo pipefail -c" [tasks."prefetch:java"] description = "Prefetch Java (Maven) dependencies" -run = "$MVN dependency:resolve --quiet" +run = "\"$MVN\" dependency:resolve --quiet" shell = "bash -euo pipefail -c" diff --git a/.mise/config.kotlin.toml b/.mise/config.kotlin.toml index 48fe02c..ba80a58 100644 --- a/.mise/config.kotlin.toml +++ b/.mise/config.kotlin.toml @@ -28,7 +28,7 @@ gradle [tasks.build-ws-kotlin-data1-module] description = "Build the kotlin-data1 workflow module (Kotlin/Wasm -> WasmGC)" dir = "services/ws-modules/kotlin-data1" -run = "$GRADLE --console=plain pkgDist" +run = "\"$GRADLE\" --console=plain pkgDist" shell = "bash -euo pipefail -c" # Namespaced aggregator picked up by the default config's globbed `check`. @@ -36,11 +36,11 @@ shell = "bash -euo pipefail -c" [tasks."check:kotlin"] description = "Run Kotlin checks (kotlinc allWarningsAsErrors via the Gradle compile)" dir = "services/ws-modules/kotlin-data1" -run = "$GRADLE --console=plain compileKotlinWasmJs" +run = "\"$GRADLE\" --console=plain compileKotlinWasmJs" shell = "bash -euo pipefail -c" [tasks."prefetch:kotlin"] description = "Prefetch Kotlin (Gradle plugin + Maven Central) dependencies" dir = "services/ws-modules/kotlin-data1" -run = "$GRADLE --console=plain dependencies" +run = "\"$GRADLE\" --console=plain dependencies" shell = "bash -euo pipefail -c" diff --git a/.mise/config.maint.toml b/.mise/config.maint.toml index 5706453..f25c27b 100644 --- a/.mise/config.maint.toml +++ b/.mise/config.maint.toml @@ -530,7 +530,9 @@ shell = "bash -euo pipefail -c" depends = ["_setup-store-tools"] description = "Publish this platform's mise tool tree to its per-platform GHCR store (maintainer-only)" run = """ -detected=$(coreutils uname -sm) +coreutils="$(mise which coreutils)" +crane="$(mise which crane)" +detected=$("$coreutils" uname -sm) case "$detected" in Darwin*arm64) plat=macos-arm64 ;; Darwin*x86_64) plat=macos-x64 ;; @@ -541,10 +543,10 @@ case "$detected" in esac ref="ghcr.io/edge-toolkit/core/mise-tools/${plat}:latest" out="target/scratch/mise-oci-${plat}" -coreutils rm -rf "$out" +"$coreutils" rm -rf "$out" MISE_ENV="$ALL_LANGS" mise install MISE_ENV="$ALL_LANGS" MISE_EXPERIMENTAL=1 mise oci build -o "$out" -crane push "$out" "$ref" +"$crane" push "$out" "$ref" """ shell = "bash -euo pipefail -c" @@ -562,12 +564,15 @@ depends = ["_setup-store-tools"] description = "Publish the linux-x64 mise tools store via docker from any host (maintainer-only)" # docker itself is a host daemon, not a mise tool; everything else comes from the depends helper. run = """ +coreutils="$(mise which coreutils)" +crane="$(mise which crane)" +gh="$(mise which gh)" +GITHUB_TOKEN="${GITHUB_TOKEN:-$("$gh" auth token)}" +export GITHUB_TOKEN ref="ghcr.io/edge-toolkit/core/mise-tools/linux-x64:latest" out="target/scratch/mise-oci-linux-x64" -GITHUB_TOKEN="${GITHUB_TOKEN:-$(gh auth token)}" -export GITHUB_TOKEN -coreutils rm -rf "$out" -coreutils mkdir -p "$out" +"$coreutils" rm -rf "$out" +"$coreutils" mkdir -p "$out" # Bare --build-arg forwards $BASE_IMAGE when set, falling back to the Dockerfile default otherwise. # (E.g. BASE_IMAGE=ubuntu:26.04 to test another base.) Keep the default: the store's consumers are the # hosted CI runners on ubuntu 24.04, and tools compiled during install (the go: backend build, any cargo: @@ -579,7 +584,7 @@ args="--platform linux/amd64 --target build --secret id=gh_token,env=GITHUB_TOKE DOCKER_BUILDKIT=1 docker build $args -t et-mise-linux . mnt="$PWD/$out:/oci-out" docker run --rm --platform linux/amd64 -e MISE_EXPERIMENTAL=1 -v "$mnt" et-mise-linux mise oci build -o /oci-out -crane push "$out" "$ref" +"$crane" push "$out" "$ref" """ shell = "bash -euo pipefail -c" @@ -595,16 +600,18 @@ shell = "bash -euo pipefail -c" depends = ["_setup-store-tools"] description = "Verify every mise-tools GHCR store is public; print settings URLs for any still private" run = """ -json=$(gh api "/orgs/edge-toolkit/packages?package_type=container" --paginate) +gh="$(mise which gh)" +jaq="$(mise which jaq)" +json=$("$gh" api "/orgs/edge-toolkit/packages?package_type=container" --paginate) filter='.[] | select(.name | startswith("core/mise-tools/"))' report='.name + ": " + .visibility' still_private='select(.visibility != "public") | .name | @uri' -echo "$json" | jaq -r "$filter | $report" -if [ -z "$(echo "$json" | jaq -r "$filter | .name")" ]; then +echo "$json" | "$jaq" -r "$filter | $report" +if [ -z "$(echo "$json" | "$jaq" -r "$filter | .name")" ]; then echo "no mise-tools packages exist yet; publish one with push-mise-tools first" >&2 exit 1 fi -private=$(echo "$json" | jaq -r "$filter | $still_private") +private=$(echo "$json" | "$jaq" -r "$filter | $still_private") if [ -z "$private" ]; then echo "all mise-tools stores are public" exit 0 diff --git a/.mise/config.toml b/.mise/config.toml index 55eed47..4fab7a6 100644 --- a/.mise/config.toml +++ b/.mise/config.toml @@ -1352,17 +1352,28 @@ description = "Build et-cli (prereq for the module-package-json steps)" run = "cargo build -p et-cli" shell = "bash -euo pipefail -c" -# Install the tools the GHCR-store tasks call, before their PATH is composed. -# A task's PATH is fixed when the task starts, so a `mise install` INSIDE a body leaves the just-installed -# bare names unresolvable ("bash: line 4: coreutils: command not found" on a cold runner, observed at -# https://github.com/edge-toolkit/core/actions/runs/31670112517/job/94352839713 on commit -# d8ad8cb99cf58a1e505cdbebc5b94f9893532b4f). A `depends` task runs before that composition, so the -# store tasks (pull-mise-tools here, the push/visibility maint tasks) get working tools on any checkout -# that has never run a full `mise install`. gh is only needed by the maint tasks, but one shared helper -# beats three near-copies. +# Install the tools the GHCR-store tasks call, so they work on a checkout that never ran a full `mise install`. +# gh is only needed by the maint tasks, but one shared helper beats three near-copies. +# +# Installing is all this buys: PATH does NOT follow. mise composes a task's PATH from what is installed when +# the `mise run` starts, and a `depends` install lands after that snapshot just as an in-body `mise install` +# does -- so a cold runner still gets "bash: line 1: coreutils: command not found" (first seen with the in-body +# form at https://github.com/edge-toolkit/core/actions/runs/31670112517/job/94352839713 on commit +# d8ad8cb99cf58a1e505cdbebc5b94f9893532b4f, then again through `depends` at +# https://github.com/edge-toolkit/core/actions/runs/31773896309/job/94685230924 on commit +# 0dd8cb009e2b81f9013d7a647092adf383391284). Every consumer therefore resolves these tools with `mise which` +# and calls them by absolute path, which needs no PATH entry at all. +# +# Keep the tool specs unquoted. This body is a single line, so on Windows it runs under mise's default +# `cmd /c` -- the repo's bash inline-shell default is set by `preinstall`, which has not run yet when CI +# restores the store. cmd.exe does not strip quotes, so a quoted spec reaches mise verbatim and it tries to +# clone a plugin from the literal name: `mise plugin:01mf02/jaq' clone https://github.com/01mf02/jaq'.git`, +# ending in `Failed to install 'aqua:01mf02/jaq'@latest`, which stranded the Windows lane's restore on commit +# 0dd8cb009e2b81f9013d7a647092adf383391284 at +# https://github.com/edge-toolkit/core/actions/runs/31773896309/job/94685231006. [tasks._setup-store-tools] description = "Install the tools the mise-tools store tasks depend on (cold-checkout helper)" -run = "mise install coreutils crane gh 'aqua:01mf02/jaq'" +run = "mise install coreutils crane gh aqua:01mf02/jaq" # Pre-populate the local mise data dir from this platform's GHCR tool store. # A maintainer publishes each platform's tool tree to ghcr.io//mise-tools/- with the maint @@ -1376,7 +1387,10 @@ run = "mise install coreutils crane gh 'aqua:01mf02/jaq'" depends = ["_setup-store-tools"] description = "Pre-populate the local mise data dir from this platform's GHCR tool store" run = """ -detected=$(coreutils uname -sm) +coreutils="$(mise which coreutils)" +crane="$(mise which crane)" +jaq="$(mise which jaq)" +detected=$("$coreutils" uname -sm) case "$detected" in Darwin*arm64) def_plat=macos-arm64 ;; Darwin*x86_64) def_plat=macos-x64 ;; @@ -1403,12 +1417,12 @@ fi # and because tar refuses to write through existing symlinks on a populated data dir ("Cannot extract # through symlink installs/http-augeas//bin", observed locally). tmp="target/scratch/pull-mise-tools" -coreutils rm -rf "$tmp" -coreutils mkdir -p "$tmp" "$data/installs" +"$coreutils" rm -rf "$tmp" +"$coreutils" mkdir -p "$tmp" "$data/installs" echo "pulling $ref" -crane pull --format=oci "$ref" "$tmp/oci" -manifest=$(jaq -r '.manifests[0].digest | ltrimstr("sha256:")' "$tmp/oci/index.json") -layers=$(jaq -r '.layers[].digest | ltrimstr("sha256:")' "$tmp/oci/blobs/sha256/$manifest") +"$crane" pull --format=oci "$ref" "$tmp/oci" +manifest=$("$jaq" -r '.manifests[0].digest | ltrimstr("sha256:")' "$tmp/oci/index.json") +layers=$("$jaq" -r '.layers[].digest | ltrimstr("sha256:")' "$tmp/oci/blobs/sha256/$manifest") # Layers that carry no mise/installs entries (the base image's) make tar exit non-zero; that miss is fine. for layer in $layers; do tar -xzf "$tmp/oci/blobs/sha256/$layer" -C "$tmp" --strip-components=1 mise/installs 2>/dev/null || true @@ -1419,12 +1433,12 @@ for d in "$tmp"/installs/*/*/; do rel="${d#"$tmp"/installs/}" dest="$data/installs/$rel" if [ ! -e "$dest" ]; then - coreutils mkdir -p "$(coreutils dirname "$dest")" - coreutils mv "$d" "$dest" + "$coreutils" mkdir -p "$("$coreutils" dirname "$dest")" + "$coreutils" mv "$d" "$dest" restored=$((restored + 1)) fi done -coreutils rm -rf "$tmp" +"$coreutils" rm -rf "$tmp" echo "restored $restored tool versions into $data/installs" echo "done; run 'mise install' to fill gaps and regenerate shims" """ diff --git a/.mise/config.zig.toml b/.mise/config.zig.toml index e9715d9..5fc1b19 100644 --- a/.mise/config.zig.toml +++ b/.mise/config.zig.toml @@ -12,6 +12,18 @@ # `clang-format` name to conda:clang-format, so conda:clang-tools omits it and only supplies clang-tidy. Both # are conda-forge's native LLVM binaries. cpplint (Google C++ style) and flawfinder (C/C++ security scanner) # have no prebuilt binary -- pipx is their only distribution. +# +# Tasks must invoke both conda binaries by absolute path (`$(mise which clang-format)`), never by bare name. +# mise 2026.8.5 (jdx/mise#11855) stopped copying conda executables into `/.mise-bins/` and writes a +# `.cmd` prefix-activation launcher there instead. Windows can only run a `.cmd` through cmd.exe, and the +# spawner starts it by bare name, so cmd.exe has to find it again on the PATH it inherited -- which on the +# Windows lane is long enough that cmd.exe discards it, leaving nothing to search: +# 'clang-format' is not recognized as an internal or external command, +# operable program or batch file. +# That took down the windows-latest check lane on commit 0dd8cb009e2b81f9013d7a647092adf383391284 at +# https://github.com/edge-toolkit/core/actions/runs/31773896309/job/94685231006. An absolute path needs no +# lookup, so the launcher runs whatever cmd.exe did with the PATH. pipx's Windows entry points are real +# `.exe`s, so cpplint and flawfinder are unaffected. "conda:clang-format" = "latest" "conda:clang-tools" = "latest" "pipx:cpplint" = "latest" @@ -53,11 +65,20 @@ run = "zig fmt services/ws-modules/ generated/zig-rest/" [tasks.clang-format] description = "Format C/C++ sources (clang-format)" -run = "git ls-files '*.c' '*.cpp' '*.h' | xargs clang-format -i --style=file:config/clang-format.yaml" +run = """ +clang_format="$(mise which clang-format)" +git ls-files '*.c' '*.cpp' '*.h' | xargs "$clang_format" -i --style=file:config/clang-format.yaml +""" +shell = "bash -euo pipefail -c" [tasks.clang-format-check] description = "Check C/C++ source formatting (clang-format)" -run = "git ls-files '*.c' '*.cpp' '*.h' | xargs clang-format --dry-run --Werror --style=file:config/clang-format.yaml" +run = """ +clang_format="$(mise which clang-format)" +git ls-files '*.c' '*.cpp' '*.h' | + xargs "$clang_format" --dry-run --Werror --style=file:config/clang-format.yaml +""" +shell = "bash -euo pipefail -c" [tasks.clang-tidy-check] description = "Lint C/C++ sources (clang-tidy)" @@ -77,8 +98,9 @@ description = "Lint C/C++ sources (clang-tidy)" # thread 'main' panicked at src/xargs/mod.rs:411:46: index out of bounds: the len is 0 but the index is 0 # (observed on PR #93's docker-linux build + check lanes). -r makes empty input a clean no-op. run = """ +clang_tidy="$(mise which clang-tidy)" git ls-files '*.c' '*.cpp' '*.h' ':!services/ws-web-runner/mingw-shim' ':!services/ws-modules/zig-*' | - xargs -r -I{} clang-tidy --config-file=config/clang-tidy.yaml {} -- {{ vars.clang_resource_arg }} + xargs -r -I{} "$clang_tidy" --config-file=config/clang-tidy.yaml {} -- {{ vars.clang_resource_arg }} llvm_ct="$(mise where 'github:mstorsjo/llvm-mingw')/bin/clang-tidy" git ls-files 'services/ws-web-runner/mingw-shim/*.c' | xargs -r -I{} "$llvm_ct" --config-file=config/clang-tidy.yaml {} -- --target=x86_64-w64-mingw32 -std=c11 @@ -93,7 +115,7 @@ else set -- {{ vars.clang_resource_arg }} "$@" fi git ls-files 'services/ws-modules/zig-*/src/*.c' 'services/ws-modules/zig-*/src/*.cpp' | - xargs -r -I{} clang-tidy --config-file=config/clang-tidy.yaml {} -- "$@" + xargs -r -I{} "$clang_tidy" --config-file=config/clang-tidy.yaml {} -- "$@" """ shell = "bash -euo pipefail -c" @@ -103,8 +125,9 @@ description = "Apply clang-tidy's machine-applicable fix-its to C/C++ sources in # Host clang-tidy for portable C/C++, llvm-mingw's for the windows mingw-shim, and a wasm32 cross-target pass # for the zig modules' C/C++. run = """ +clang_tidy="$(mise which clang-tidy)" git ls-files '*.c' '*.cpp' '*.h' ':!services/ws-web-runner/mingw-shim' ':!services/ws-modules/zig-*' | - xargs -r -I{} clang-tidy --fix --config-file=config/clang-tidy.yaml {} -- {{ vars.clang_resource_arg }} + xargs -r -I{} "$clang_tidy" --fix --config-file=config/clang-tidy.yaml {} -- {{ vars.clang_resource_arg }} llvm_ct="$(mise where 'github:mstorsjo/llvm-mingw')/bin/clang-tidy" git ls-files 'services/ws-web-runner/mingw-shim/*.c' | xargs -r -I{} "$llvm_ct" --fix --config-file=config/clang-tidy.yaml {} -- --target=x86_64-w64-mingw32 -std=c11 @@ -119,7 +142,7 @@ else set -- {{ vars.clang_resource_arg }} "$@" fi git ls-files 'services/ws-modules/zig-*/src/*.c' 'services/ws-modules/zig-*/src/*.cpp' | - xargs -r -I{} clang-tidy --fix --config-file=config/clang-tidy.yaml {} -- "$@" + xargs -r -I{} "$clang_tidy" --fix --config-file=config/clang-tidy.yaml {} -- "$@" """ shell = "bash -euo pipefail -c" diff --git a/.mise/mise.lock b/.mise/mise.lock index 21461d3..e1a1585 100644 --- a/.mise/mise.lock +++ b/.mise/mise.lock @@ -845,6 +845,7 @@ checksum = "sha256:c6faa02823d9ad2c99d0d377a778daa925e7ad2ef9beb96a180236fd2f551 url = "https://github.com/owenlamont/ryl/releases/download/v0.19.1/ryl-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/owenlamont/ryl/releases/assets/451334907" provenance = "github-attestations" +provenance_verified = true [[tools."github:prometheus-community/windows_exporter"]] version = "0.31.7" @@ -1113,6 +1114,7 @@ url = "https://downloads.openobserve.ai/releases/openobserve/v0.91.5/openobserve url = "https://downloads.openobserve.ai/releases/openobserve/v0.91.5/openobserve-v0.91.5-darwin-amd64.tar.gz" [tools."http:openobserve"."platforms.windows-x64"] +checksum = "blake3:297959bde6dd30397317a6be78f3d74463aeab5f555dc44283f6a21231b452c3" url = "https://downloads.openobserve.ai/releases/openobserve/v0.91.5/openobserve-v0.91.5-windows-amd64.zip" [[tools."http:rp-wasm"]] @@ -1154,6 +1156,7 @@ url = "https://github.com/rustfs/rustfs/releases/download/1.0.0-beta.12/rustfs-l url = "https://github.com/rustfs/rustfs/releases/download/1.0.0-beta.12/rustfs-macos-aarch64-v1.0.0-beta.12.zip" [tools."http:rustfs"."platforms.windows-x64"] +checksum = "blake3:d79f0eddd9c9c68df0c4c4c7f744639bf2ded714be0ccd2f52f3f27c372e2630" url = "https://github.com/rustfs/rustfs/releases/download/1.0.0-beta.12/rustfs-windows-x86_64-v1.0.0-beta.12.zip" [[tools.ls-lint]] diff --git a/.mise/mise.r.lock b/.mise/mise.r.lock index 0560857..5ac2c6c 100644 --- a/.mise/mise.r.lock +++ b/.mise/mise.r.lock @@ -18,4 +18,5 @@ url = "https://github.com/r-wasm/webr/releases/download/v0.6.0/webr-0.6.0.tar.gz url = "https://github.com/r-wasm/webr/releases/download/v0.6.0/webr-0.6.0.tar.gz" [tools."http:webr"."platforms.windows-x64"] +checksum = "blake3:764c56176d67d64b858325fcc245d653ef5993dc01df3e7f8929448439d9785d" url = "https://github.com/r-wasm/webr/releases/download/v0.6.0/webr-0.6.0.tar.gz" diff --git a/.mise/mise.windows.lock b/.mise/mise.windows.lock index 52b71cb..ddd7b1a 100644 --- a/.mise/mise.windows.lock +++ b/.mise/mise.windows.lock @@ -1043,19 +1043,19 @@ checksum = "sha256:e04493f469fd3861e119a603700cfeafe8da0090aed39b547ad3975b21f48 url = "https://github.com/edge-toolkit/core/releases/download/gnupg-w32-v1/2.5.20_20260513-x86_64-pc-windows.tar.gz" [[tools.rust]] -version = "stable-x86_64-pc-windows-gnullvm" +version = "nightly-2026-08-05-x86_64-pc-windows-gnullvm" backend = "core:rust" [tools.rust.options] -components = "clippy,rust-analyzer" +components = "rust-src,rustfmt" profile = "minimal" targets = "wasm32-unknown-unknown,wasm32-wasip2" [[tools.rust]] -version = "nightly-2026-08-05-x86_64-pc-windows-gnullvm" +version = "stable-x86_64-pc-windows-gnullvm" backend = "core:rust" [tools.rust.options] -components = "rust-src,rustfmt" +components = "clippy,rust-analyzer" profile = "minimal" targets = "wasm32-unknown-unknown,wasm32-wasip2" From a00c7a86f64ed8612399a88c8d5ae904b5fa2b0e Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Fri, 14 Aug 2026 18:34:32 +0800 Subject: [PATCH 6/6] relocatable oci --- .mise/config.maint.toml | 4 ++-- .mise/config.toml | 51 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/.mise/config.maint.toml b/.mise/config.maint.toml index f25c27b..0e2c786 100644 --- a/.mise/config.maint.toml +++ b/.mise/config.maint.toml @@ -545,7 +545,7 @@ ref="ghcr.io/edge-toolkit/core/mise-tools/${plat}:latest" out="target/scratch/mise-oci-${plat}" "$coreutils" rm -rf "$out" MISE_ENV="$ALL_LANGS" mise install -MISE_ENV="$ALL_LANGS" MISE_EXPERIMENTAL=1 mise oci build -o "$out" +MISE_ENV="$ALL_LANGS" mise run oci-build-store "$out" "$crane" push "$out" "$ref" """ shell = "bash -euo pipefail -c" @@ -583,7 +583,7 @@ args="--platform linux/amd64 --target build --secret id=gh_token,env=GITHUB_TOKE # shellcheck disable=SC2086 DOCKER_BUILDKIT=1 docker build $args -t et-mise-linux . mnt="$PWD/$out:/oci-out" -docker run --rm --platform linux/amd64 -e MISE_EXPERIMENTAL=1 -v "$mnt" et-mise-linux mise oci build -o /oci-out +docker run --rm --platform linux/amd64 -v "$mnt" et-mise-linux mise run oci-build-store /oci-out "$crane" push "$out" "$ref" """ shell = "bash -euo pipefail -c" diff --git a/.mise/config.toml b/.mise/config.toml index 4fab7a6..cae302d 100644 --- a/.mise/config.toml +++ b/.mise/config.toml @@ -1375,14 +1375,57 @@ shell = "bash -euo pipefail -c" description = "Install the tools the mise-tools store tasks depend on (cold-checkout helper)" run = "mise install coreutils crane gh aqua:01mf02/jaq" +# Build the OCI layout for a mise-tools store, carrying only relocatable tools. +# Shared by every push-mise-tools* task (native and docker) so all platforms' stores hold the same set of +# backends, and lives here rather than in config.maint.toml because the docker publisher runs it INSIDE the +# build container, where only the always-loaded configs exist. +# +# A tool is relocatable when its install tree is a plain extracted archive: aqua, github, http and ubi all +# unpack a tarball and stop, so moving the tree to another machine's data dir works. Every other backend +# writes the publishing machine's absolute paths into the tree at install time, which then breaks on a +# consumer whose $HOME differs -- conda emits `.mise-bins/` wrappers holding +# +# export CONDA_PREFIX='/home//.local/share/mise/installs/conda-/' +# exec '/home//.local/share/mise/installs/conda-//bin/' +# +# so a restored `clang-format` dies with `exec: /home//...: not found` (exit 127), and pipx does +# the same through venv shebangs + pyvenv.cfg. `mise install` cannot repair those: it treats a tool with an +# install dir as installed, so a poisoned tree survives every later install. Excluding them costs nothing -- +# the store exists to spare api.github.com the per-tool release/attestation lookups that aqua, github and +# http tools make, and conda / pipx / cargo / go / core-runtime installs never touch that API. +[tasks.oci-build-store] +depends = ["_setup-store-tools"] +description = "Build the mise-tools OCI layout for the current toolset, relocatable backends only" +run = """ +coreutils="$(mise which coreutils)" +jaq="$(mise which jaq)" +out="${usage_out:?output directory required}" +# Prefixed names carry their own backend; bare ones are registry short names to resolve. +# `mise registry ` prints every backend for the name, preferred first, so take the first field. +disabled="" +for tool in $(mise ls --current --json | "$jaq" -r 'keys[]'); do + case "$tool" in + *:*) backend="$tool" ;; + *) backend="$(mise registry "$tool" | "$coreutils" head -n1 | "$coreutils" cut -d' ' -f1)" ;; + esac + case "$backend" in + aqua:* | github:* | http:* | ubi:*) ;; + *) disabled="${disabled:+$disabled,}$tool" ;; + esac +done +echo "store excludes non-relocatable tools: ${disabled:-}" +MISE_DISABLE_TOOLS="$disabled" MISE_EXPERIMENTAL=1 mise oci build -o "$out" +""" +shell = "bash -euo pipefail -c" +usage = 'arg "" help="Output directory for the OCI image layout"' + # Pre-populate the local mise data dir from this platform's GHCR tool store. # A maintainer publishes each platform's tool tree to ghcr.io//mise-tools/- with the maint # env's push-mise-tools task (`mise oci push`, one content-addressed layer per tool); this flattens that # image with `crane export` straight into the local data dir's installs/, so a fresh `mise install` -# afterwards only fills the gaps and regenerates shims instead of downloading everything. Tools that bake -# absolute paths at install time (e.g. pipx venv shebangs) carry the publishing machine's home dir -- -# reinstall any misbehaving one with `mise install --force `. Private packages need a one-time -# `docker login ghcr.io` (or `crane auth login ghcr.io`). +# afterwards only fills the gaps and regenerates shims instead of downloading everything. The store carries +# only relocatable backends, so a restored tree needs no repair; everything else installs cold as usual. +# Private packages need a one-time `docker login ghcr.io` (or `crane auth login ghcr.io`). [tasks.pull-mise-tools] depends = ["_setup-store-tools"] description = "Pre-populate the local mise data dir from this platform's GHCR tool store"