gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

⎇ main recipes/jj.toml TOML · 102 siq'ikuna · 6.4 KB Ñawpa willay ch'uya

recetas: typst 0.15.1 y jj 0.45.1 — CLI en Rust puro, cero deps C, y cada una con su prueba de que HACE algo 8bca2d966 · Sergio ·

1# jj (jujutsu) — control de versiones moderno, compatible con git, en Rust puro
2#
3# ── POR QUÉ ENTRA ──────────────────────────────────────────────────────────────────────────────
4# 32.018★, Apache-2.0, 549 crates, un solo binario sin interfaz gráfica. Lo que lo hace barato para
5# esta casa sale del `Cargo.lock` y no del README: **habla git sin `libgit2`**. Son **53 crates
6# `gix-*`** (gitoxide, git reimplementado en Rust) y **cero `git2`/`libgit2-sys`** ⇒ ninguna receta C
7# nueva, ningún `.pc` que buscar. El único C del árbol es `libmimalloc-sys`, que trae su propio
8# fuente y lo compila con el crate `cc` — de ahí el envoltorio de `AR`.
9#
10# No reemplaza a `git`, que sigue siendo el que usa todo el repo y la granja: convive con él sobre
11# los mismos repositorios.
12#
13# ── ⚠ POR QUÉ 0.45.1 Y NO LA ÚLTIMA, QUE ES LO MÁS IMPORTANTE DE ESTA RECETA ──────────────────
14# La release vigente al 2026-10-13 es **0.46.0**, y **no entra**: declara `rust-version = "1.97.1"`
15# y el lab de takana trae **rustc 1.97.0** (`docs/state/lab-toolchain.lock`: `rust-1.97.0-r0`).
16# Son dos números de parche y es un muro duro: cargo se niega antes de compilar nada.
17#
18# El salto es de upstream y es brusco —0.45.1 pedía **1.89**, o sea ocho versiones menos— así que
19# esto no es «quedarse atrás»: es el techo del lab, que es [[cargo-recipe-msrv-ceiling]]. Y subir el
20# lab **no es una opción local**: el lab entra en `hash_inputs`, así que un `bootstrap-devfs.sh
21# --relock` por una receta re-hashea el corpus entero. Cuando el lab suba por otra razón, esta receta
22# puede ir a 0.46.0 cambiando dos líneas.
23name    = "jj"
24version = "0.45.1"
25license = "Apache-2.0"
26
27[source]
28# `v0.45.1`. Se pinea el SHA del tag: un tag se mueve.
29repo   = "https://github.com/jj-vcs/jj"
30commit = "7c41cdeb16b6b321c64e789a966b6adf723816a5"
31# No committea `vendor/` ni usa `[patch.crates-io]` ⇒ el vendoreo va al `vendor/` por defecto sin
32# pisarle nada (la trampa que mató a `pdfcraft`; comprobado en el árbol del tag).
33
34[build]
35compiler = "zig-cc"
36target   = "x86_64-linux-musl"
37# `static`: es una herramienta de línea de comandos y no carga nada por `dlopen`.
38link     = "static"
39# Los features por defecto de `jj-cli` son `["watchman", "git"]` y se dejan: `git` es la razón de
40# existir del binario acá, y `watchman` es sólo un CLIENTE en Rust del vigía de ficheros de Meta —si
41# el demonio no está, la función queda apagada y no pasa nada.
42flags    = ["-p", "jj-cli", "--bin", "jj"]
43
44[build.phases]
45compile = '''
46set -e
47# El envoltorio de `AR` no es decorativo: el default de zig-cc exporta `AR="zig ar"` CON ESPACIO y el
48# crate `cc` —que acá compila `libmimalloc-sys`— ejecuta `$AR` como un binario ÚNICO ⇒ busca uno
49# llamado «zig ar» y falla. Cicatriz de `mise.toml`.
50printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec zig cc -mcpu=baseline "$@"' > "$PWD/.hammer-zig-cc"
51chmod +x "$PWD/.hammer-zig-cc"
52printf '%s\n' '#!/bin/sh' 'exec zig ar "$@"' > "$PWD/.hammer-zig-ar"
53chmod +x "$PWD/.hammer-zig-ar"
54RF="-C linker=$PWD/.hammer-zig-cc"
55rustc -vV | grep -q 'host: .*-alpine-' || RF="$RF -C link-self-contained=no"
56CC="$PWD/.hammer-zig-cc" AR="$PWD/.hammer-zig-ar" RUSTFLAGS="$RF" cargo rustc --release --locked --offline -p jj-cli --bin jj -- -C target-feature=+crt-static -C relocation-model=static
57'''
58install = '''
59set -e
60test -x target/release/jj || { echo "no hay target/release/jj — ¿cambió el nombre del bin?" >&2; exit 1; }
61
62# ── LA PRUEBA DE QUE NO ES UN NOMBRE: QUE CREE UN REPOSITORIO Y LO LEA ────────────────────────
63# `--version` sólo prueba que el binario arranca. Lo que se mide acá es que el **backend de git en
64# Rust** funciona de verdad: crear un repo, escribir un fichero y que `jj` lo vea como cambio. Si
65# eso no pasa, esta receta no debe sellar (memoria: «subcomando SIN DRIVER = binario inerte»).
66export HOME="$PWD/.smoke/home" JJ_USER="takana" JJ_EMAIL="takana@localhost" JJ_CONFIG="$PWD/.smoke/jj.toml"
67mkdir -p "$HOME" "$PWD/.smoke/repo"
68: > "$JJ_CONFIG"
69./target/release/jj --version > "$PWD/.smoke/v" 2>&1 || { echo "jj --version falló:" >&2; cat "$PWD/.smoke/v" >&2; exit 1; }
70grep -q "^jj 0.45.1" "$PWD/.smoke/v" || { echo "jj dice '$(cat "$PWD/.smoke/v")' y la receta pinea 0.45.1" >&2; exit 1; }
71./target/release/jj git init "$PWD/.smoke/repo" > "$PWD/.smoke/init" 2>&1 || { echo "jj git init falló:" >&2; cat "$PWD/.smoke/init" >&2; exit 1; }
72test -d "$PWD/.smoke/repo/.jj" || { echo "jj git init salió 0 y NO dejó .jj — el caso «sale bien y no hizo nada»" >&2; exit 1; }
73echo hola > "$PWD/.smoke/repo/f.txt"
74./target/release/jj -R "$PWD/.smoke/repo" status > "$PWD/.smoke/st" 2>&1 || { echo "jj status falló:" >&2; cat "$PWD/.smoke/st" >&2; exit 1; }
75grep -q "f.txt" "$PWD/.smoke/st" || { echo "jj status no vio el fichero nuevo:" >&2; cat "$PWD/.smoke/st" >&2; exit 1; }
76echo "smoke: jj creó el repo, vio el cambio, y todo con gitoxide (sin libgit2)"
77
78install -Dm755 target/release/jj /out/usr/bin/jj
79install -Dm644 LICENSE /out/usr/share/licenses/jj/LICENSE
80
81# Completado de shell y manual, generados por el propio binario. ⚠ A diferencia de todo lo de
82# arriba, un fallo acá NO tumba el build y es deliberado: el contrato de esta receta es el binario;
83# el completado es comodidad. Si el subcomando cambia de nombre en un bump, se quiere ver el aviso
84# en el log, no perder el artefacto entero.
85for sh in bash zsh fish; do
86  if ./target/release/jj util completion "$sh" > "$PWD/.smoke/jj.$sh" 2>/dev/null && [ -s "$PWD/.smoke/jj.$sh" ]; then
87    case "$sh" in
88      bash) install -Dm644 "$PWD/.smoke/jj.$sh" /out/usr/share/bash-completion/completions/jj ;;
89      zsh)  install -Dm644 "$PWD/.smoke/jj.$sh" /out/usr/share/zsh/site-functions/_jj ;;
90      fish) install -Dm644 "$PWD/.smoke/jj.$sh" /out/usr/share/fish/vendor_completions.d/jj.fish ;;
91    esac
92  else
93    echo "aviso: 'jj util completion $sh' no produjo nada — ¿cambió el subcomando? el binario SÍ está" >&2
94  fi
95done
96test -s /out/usr/bin/jj
97'''
98
99[deps]
100# Ninguna, y es medido: sin `libgit2-sys`, sin `openssl-sys`, sin `zlib` del sistema. `libmimalloc-sys`
101# compila su propio C con el crate `cc`. Un `[deps]` vacío acá significa «no necesita nada».
102build = []