refactor(cli)!: split the contributor tool out of the ludic CLI
`ludic help` ended with a section titled "contributing to the toolchain itself", listing bootstrap, reseed, docs-gen and release tasks. None of that is available to someone who installed the language — those tasks need the repository — so the shipped tool was advertising work its user cannot do, in a namespace they have to read past to find `new` and `run`. The tasks move to a second program, dev.ludic -> bin/ludic-dev, built from a checkout and excluded from every release artifact. `ludic` keeps the project and package commands and nothing else; `ludic dev …` now explains where the tasks went instead of failing as an unknown command. What this shook out: the two programs share prelude/build/project/pkg, so the helpers each had accreted in whichever file first needed them — cc(), ensure_ludicc, the string functions, title_case, cmd_version — moved to where both can see them. The argument-shift indirection added for the `dev` namespace is gone with the namespace, so commands read argv directly again. `ludic-dev test` asserts the split rather than trusting it: the staged install must build a project, and `ludic dev build` there must fail while naming ludic-dev. install.sh keeps building older tags, whose bootstrap goes through main.ludic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f369fbd227
commit
e175619543
46 changed files with 630 additions and 558 deletions
|
|
@ -1,13 +1,13 @@
|
|||
# release.ludic — versioning + release cutting for the toolchain.
|
||||
#
|
||||
# ludic version print the toolchain version (from the VERSION file)
|
||||
# ludic dev release [level] cut a release: aggregate changes/ into CHANGELOG.md,
|
||||
# ludic-dev release [level] cut a release: aggregate changes/ into CHANGELOG.md,
|
||||
# bump VERSION, commit, and tag vX.Y.Z. `level` is
|
||||
# major|minor|patch; omitted, it is derived from the
|
||||
# highest `bump:` among the pending changesets.
|
||||
# ludic dev release [level] --dry-run render the changelog section to stdout and
|
||||
# ludic-dev release [level] --dry-run render the changelog section to stdout and
|
||||
# stop: nothing is written, committed, tagged or pushed.
|
||||
# ludic dev release [level] --publish ...then push main + the tag and create a
|
||||
# ludic-dev release [level] --publish ...then push main + the tag and create a
|
||||
# Forgejo release with source + toolchain tarballs.
|
||||
# Needs FORGEJO_TOKEN in the environment.
|
||||
#
|
||||
|
|
@ -49,24 +49,6 @@ function compute_next(cur: pointer, level: pointer) -> pointer {
|
|||
return `{maj}.{min}.{p2}`
|
||||
}
|
||||
|
||||
# ludic version — report the version of the toolchain this CLI belongs to.
|
||||
#
|
||||
# It asks the compiler (proving that path works), addressed through ludic_home()
|
||||
# rather than as a relative bin/ludicc: `ludic version` is run from a user's
|
||||
# project, not from the toolchain's own directory, and looking beside the current
|
||||
# directory found nothing there and reported "(version unknown)" for a perfectly
|
||||
# good install.
|
||||
function cmd_version() -> int {
|
||||
let cc = ludicc()
|
||||
if is_exec(cc) {
|
||||
run(`{cc} --version`)
|
||||
return 0
|
||||
}
|
||||
let v = read_file(`{ludic_home()}VERSION`)
|
||||
if v != null { print(`ludic {s_trim(v)}`); return 0 }
|
||||
print(`ludic {read_version_or("(version unknown)")}`)
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---- changelog rendering ----------------------------------------------------
|
||||
#
|
||||
|
|
@ -241,16 +223,16 @@ function build_section(ver: pointer) -> void {
|
|||
write_file(tmp_path("rel_section.md"), render_section(ver, date, "changes"))
|
||||
}
|
||||
|
||||
# ludic dev changelog-render <version> <date> <dir> — print the CHANGELOG section that
|
||||
# ludic-dev changelog-render <version> <date> <dir> — print the CHANGELOG section that
|
||||
# `dir`'s changesets would produce. Used to re-render the sections of releases
|
||||
# cut before the renderer preserved markdown structure: check the changesets out
|
||||
# of the tag's parent commit, point this at them, and splice the result back in.
|
||||
function cmd_changelog_render() -> int {
|
||||
if arg_total() < 5 {
|
||||
err("usage: ludic dev changelog-render <version> <date> <changesets-dir>\n")
|
||||
if arg_count() < 5 {
|
||||
err("usage: ludic-dev changelog-render <version> <date> <changesets-dir>\n")
|
||||
return 1
|
||||
}
|
||||
out(render_section(arg_n(2), arg_n(3), arg_n(4)))
|
||||
out(render_section(arg(2), arg(3), arg(4)))
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +240,7 @@ function cmd_changelog_render() -> int {
|
|||
# release section (or at the end of the header if this is the first release).
|
||||
function prepend_changelog() -> void {
|
||||
if not file_exists("CHANGELOG.md") {
|
||||
run("printf '# Changelog\n\nAll notable changes to the Ludic toolchain, newest first. Generated from the\nchangesets under changes/ by ludic dev release; do not edit released sections by hand.\n\n' > CHANGELOG.md")
|
||||
run("printf '# Changelog\n\nAll notable changes to the Ludic toolchain, newest first. Generated from the\nchangesets under changes/ by ludic-dev release; do not edit released sections by hand.\n\n' > CHANGELOG.md")
|
||||
}
|
||||
let ln = capture_line("grep -n '^## ' CHANGELOG.md | head -1 | cut -d: -f1")
|
||||
if (ln == "") {
|
||||
|
|
@ -297,11 +279,11 @@ function changelog_section(ver: pointer) -> pointer {
|
|||
return sslice(text, start, n)
|
||||
}
|
||||
|
||||
# ludic dev changelog-section <version> — print that release's CHANGELOG section.
|
||||
# ludic-dev changelog-section <version> — print that release's CHANGELOG section.
|
||||
function cmd_changelog_section() -> int {
|
||||
if arg_total() < 3 { err("usage: ludic dev changelog-section <version>\n"); return 1 }
|
||||
let sec = changelog_section(arg_n(2))
|
||||
if slen(sec) == 0 { err(`changelog-section: no section for v{arg_n(2)} in CHANGELOG.md\n`); return 1 }
|
||||
if arg_count() < 3 { err("usage: ludic-dev changelog-section <version>\n"); return 1 }
|
||||
let sec = changelog_section(arg(2))
|
||||
if slen(sec) == 0 { err(`changelog-section: no section for v{arg(2)} in CHANGELOG.md\n`); return 1 }
|
||||
out(sec)
|
||||
return 0
|
||||
}
|
||||
|
|
@ -343,7 +325,7 @@ function build_artifacts(ver: pointer) -> bool {
|
|||
let stage = `{tmp_dir()}/{name}`
|
||||
run(`rm -rf {stage} && mkdir -p {stage}/bin`)
|
||||
if not shq(`cp bin/ludic bin/ludicc bin/ludic-fmt bin/ludic-lsp {stage}/bin/`) {
|
||||
err("release: the toolchain is not built (run: ludic dev build)\n")
|
||||
err("release: the toolchain is not built (run: ludic-dev build)\n")
|
||||
return false
|
||||
}
|
||||
run(`cp -R runtime {stage}/runtime`)
|
||||
|
|
@ -366,10 +348,10 @@ function build_artifacts(ver: pointer) -> bool {
|
|||
return true
|
||||
}
|
||||
|
||||
# ludic dev publish [vX.Y.Z] — publish an already-tagged release: build the artifacts,
|
||||
# ludic-dev publish [vX.Y.Z] — publish an already-tagged release: build the artifacts,
|
||||
# take the notes from that version's CHANGELOG section, and create the Forgejo
|
||||
# release. Defaults to the version in VERSION. This is what CI runs on a tag
|
||||
# push, and what `ludic dev release --publish` calls once it has tagged.
|
||||
# push, and what `ludic-dev release --publish` calls once it has tagged.
|
||||
function publish_tag(ver: pointer) -> int {
|
||||
if getenv_or("FORGEJO_TOKEN", "") == "" { err("publish: set FORGEJO_TOKEN (a Forgejo access token)\n"); return 1 }
|
||||
let body = tmp_path("rel_notes.md")
|
||||
|
|
@ -386,8 +368,8 @@ function publish_tag(ver: pointer) -> int {
|
|||
|
||||
function cmd_publish() -> int {
|
||||
var ver = read_version_or("")
|
||||
if arg_total() >= 3 {
|
||||
var a = arg_n(2)
|
||||
if arg_count() >= 3 {
|
||||
var a = arg(2)
|
||||
if s_starts(a, "v") { a = sslice(a, 1, slen(a)) }
|
||||
ver = a
|
||||
}
|
||||
|
|
@ -409,8 +391,8 @@ function cmd_release() -> int {
|
|||
var publish = false
|
||||
var dry = false
|
||||
var ai = 2
|
||||
while ai < arg_total() {
|
||||
let a = arg_n(ai)
|
||||
while ai < arg_count() {
|
||||
let a = arg(ai)
|
||||
if (a == "--publish") { publish = true }
|
||||
else { if (a == "--dry-run") { dry = true }
|
||||
else { if (a == "major") or (a == "minor") or (a == "patch") { level = a }
|
||||
|
|
@ -437,7 +419,7 @@ function cmd_release() -> int {
|
|||
let sec = read_file(tmp_path("rel_section.md"))
|
||||
if sec != null { out(sec) }
|
||||
print("")
|
||||
print(` dry run — nothing written. cut it with: ludic dev release {level}`)
|
||||
print(` dry run — nothing written. cut it with: ludic-dev release {level}`)
|
||||
return 0
|
||||
}
|
||||
prepend_changelog()
|
||||
|
|
@ -452,7 +434,7 @@ function cmd_release() -> int {
|
|||
print(` committed + tagged v{ver}`)
|
||||
|
||||
if publish { return publish_release(ver) }
|
||||
print(` local release ready. publish with: FORGEJO_TOKEN=… ludic dev release {level} --publish`)
|
||||
print(` local release ready. publish with: FORGEJO_TOKEN=… ludic-dev release {level} --publish`)
|
||||
print(` (or push: git push origin HEAD && git push origin v{ver})`)
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue