Repository navigation
Releases: dnf/hecatron
Release list
hecatron 0.8.0
-
hct audit --output jsonlists every finding: a top-level
vulnerabilitiesarray, one record per finding (component, language,
package, installed version, fixed versions, advisory ID and aliases,
severity, title, link, auditor, lock file), complete rather than the
log's last lines injobs[].errors; the secret scan's aresecrets.
Fixed versions come from each auditor (pip-audit, govulncheck,
OSV-Scanner, bundler-audit) or the advisory's patched or vulnerable range
(npm, pnpm, Yarn, Bun, Composer); SARIF results name them too.npm audit's findings show the installed version, read from
package-lock.json. -
Nothing to audit is skipped, not passed: a component without a lock
file its auditor reads is shown as skipped, with why and how to write
one (for Gradle,gradle dependencies --write-locks), in human, agent
and JSON output ("status": "skipped","reason"), JUnit (<skipped>),
SARIF (a note) and the job summary. It doesn't fail the audit, but it
isn't counted as passed. -
OSV-Scanner audits only the language's own lock files: Swift reads
Package.resolved, RustCargo.lock, the JVM languagespom.xmland
Gradle's lock files, and so on, each named with--lockfile, instead of
a recursive scan that reported every ecosystem's packages (a root
package-lock.json's npm findings in Swift's audit) once per language.
Dependencies, build output and nested components of the same language
are left out. -
hct licensesandhct sbomtake--timeout DURATION(and
HECATRON_TIMEOUT), withhct audit's syntax:90s,30m,1h30m, a
number of seconds,0for none. Past it, OSV-Scanner is stopped and the
command exits 1. There's no limit by default. -
hct licenseslists the project's own packages apart, out of the
dependencies, the unknown count and the policy: uv's editable root (a dev
version like0.27.0.post18.dev0+g1a2b3c4used to show asUNKNOWN) and
workspace members, npm workspaces, Cargo workspace members, a
Gemfile.lock'sPATHgems, Go'sreplacewith a directory, Composer
path repositories, NuGet project references, Poetry directory
dependencies, and any package a component's manifest names. JSON:own. -
hct licensesclassifies copyleft. Each package's JSON has
licenseIds(the expression's identifiers) andcopyleft(none,
weak,strong,network,maximal,unclassified, orunknown),
from Blue Oak Council's copyleft and permissive lists, for the expression
as a whole (OR: the least restrictive choice;AND: the most
restrictive part). Licenses are rolled up per identifier too
(licenseIds, soApache-2.0 AND LGPL-3.0-or-latercounts for each),
every rollup says its class, and the summary counts copyleft packages
(copyleftin JSON). -
hct licensesandhct sbomwarn of a lock file outside every
component of its language (a rootpackage-lock.jsonin a monorepo
whose components are its subdirectories), whose packages they don't read. -
The "detected" note is for a person at a terminal.
... has no .hecatron/config.json; detected javascriptis no longer printed when
stderr isn't a terminal, with--outputor--jsonorHECATRON_OUTPUT
naming another format than human, in an agent's session or in GitHub
Actions, as the update notice already wasn't (which now also heeds
--json).hct statusstill says the config is detected. -
Skipped is a third outcome for every run command, not a pass (#236):
a hook with nothing to run or that can't run (no tests, no linter or
docs tool configured, no benchmarks, nothing to build or clean, nothing
to audit) callsskip "reason"fromlanguages/_hooks.sh, which now
marks the component skipped (HECATRON_SKIPPED, the filehct audit's
nothing-to-audit uses) instead of passed. A language without a hook for
the command (hct docs go,hct lintof a language whose build is its
lint) is skipped too:go: docs skipped: no docs tool for go. Every
default hook that already said "(skipping)" now reports skipped, in
human output (- go ... skipped,3/3 passed, 1 skipped, then why),
agent output (SKIP <lang> (<dirs>): <reason>), JSON ("status": "skipped","reason"), JUnit (<skipped message=...>), the job summary
andhct status(test: skipped 2h ago). A component skips only when
its hook does, lint only when its format check skips too, and a hook run
in several directories below (run_hook_in) only when each of them
skips. Skipped jobs are recorded inhistory.logwith status 77, so a
skipped run neither sets a job's time limit nor runs it first. Writing
project hooks: hooks. -
--fail-on-skip(orHECATRON_FAIL_ON_SKIP=1) on every run
command: exit 1 when nothing ran, every job skipped, not installed or
disabled, for CI that must have tested something
(output). -
hct format --checkinstalls nothing: where the formatter comes from
the project's dependencies (Prettier, Biome, RuboCop, Standard, PHP CS
Fixer, Pint,mix format's plugins) and they aren't installed, the check
(and the one inhct lint) skips the component, saying it wasn't checked,
instead of runningnpm ci,bundle install,composer installormix deps.get.hct formatstill installs them first. -
--no-depsforhct installandhct doctor --fix: repairs
toolchains without installing the project's dependencies. -
hct doctor --output json: each language's problems and notes, for
scripts and agents. -
hct install javascriptinstalls the Node.js version the project
resolves, as TypeScript's already did; on macOS it installed only
Homebrew's latest, andhct doctorwent on reporting the version missing. -
Python: Homebrew's versioned Pythons (
python@3.11) no longer count as
installed. They have nobin/python3, so uv, pointed at it, failed with
"No interpreter found" inhct outdated,hct formatandhct audit
whilehct doctorsaid ok; now the installer installs uv's own. -
hct outdatedfails when the tool fails without a report, rather than
reporting nothing outdated. -
Ruby's
hct doctorsays when the Gemfile's gems aren't installed
(hct deps ruby), or Bundler isn't, rather than "bundle not installed". -
Node.js apps without tests are JavaScript: a
package.jsoncounts
when it has runtimedependencies, an entry point (main,bin,
exports,module,browser),workspaces, or astart,dev,
serveortestscript, instead of only with atestscript, so an
Express app is detected and audit, outdated and licenses cover it. A
package.jsoncarrying only tooling (husky, commitlint, Prettier in
devDependencies) still isn't a language.hct testskips a package
with no test script ("package.json has no test script") instead of
failing. -
Python apps with only a
requirements.txtare Python, where*.py
files sit beside it or below (one for a docs build in a Go repository
isn't). Their tests run with the requirements' packages
(uv run --with-requirements), and skip where there are no test files. -
Xcode apps are Swift or Objective-C: an
*.xcodeprojor
*.xcworkspacewith*.swiftsources is Swift, with*.mor*.mm
Objective-C, without aPackage.swiftor Podfile. On macOShct build
runsxcodebuildon the root workspace (CocoaPods') or project, with
the scheme named like it, for a generic destination its supported
platforms give (generic/platform=macOS, elseiOS Simulator...), into
.build/xcode, whichhct cleanremoves; where it can't tell (no such
scheme, several projects), it skips, saying to write a hook. An app with
both Swift and Objective-C builds once, with Swift's hook. Tests skip,
saying to write a hook with the scheme and simulator destination, and
the app's local Swift packages aren't tested on the Mac with SwiftPM;
nor, on macOS, is any package whoseplatforms:leaves out macOS. CocoaPods'Pods/and
Carthage'sCarthage/are vendored code to detection. -
An Xcode project below the root is found: a language found only in
and below a directory holding its Xcode project gets that directory
(hct initwritesimplPaths), so GoMap'ssrc/GoMap.xcodeproj
builds instead of failing with "no Package.swift here, below, or above". -
Apps below a package build too: Swift's build and clean act on the
rootPackage.swift, then on each app below with an Xcode project of its
own and Swift sources (firefox-ios'Client.xcodeprojand
Blockzilla.xcodeproj, isowords'App/isowords.xcodeproj), outside
examples, tests and vendored code (ThirdParty/,third_party/,
Vendor/,External/,Pods/,Carthage/: firefox-ios'
ThirdParty/Deferredno longer builds as an app). Finding them scans
the directories instead of detecting every file's language, which made
hct clean swifttake seconds on firefox-ios. Objective-C found only
inside such apps leaves them to Swift, and its other hooks skip at the
root instead of failing with "no CMakeLists.txt, meson.build, configure
or Makefile". Detection skipsThirdParty/andthird-party/as it does
third_party/. -
An app beside its package builds as the app: where an Xcode project
with an application target sits beside aPackage.swift(isowords'
App/), Swift's build and clean are the Xcode app's wherexcodebuild
is installed; SwiftPM keeps the package's tests where it builds on the
host. -
Xcode builds read the project's files: where hct's binary runs the
hooks, the workspace or project, the scheme and the destination come
from the project's files (hct _xcode plan), and xcodebuild decides
only what they leav...
hecatron 0.7.0
-
curl … | shon macOS says how to puthcton PATH in the current
shell, instead of "Ready: try 'hct help'": macOS'sshis bash, and
install.sh took bash reading it from a pipe for being sourced. -
Homebrew doesn't stop to ask:
hct installandhct doctor --fix
setHOMEBREW_NO_ASK=1for brew, which asks "Do you want to proceed
with the installation?" before each install by default in current
versions, and hide its environment-variable hints. -
hct doctorchecks the languages at once, withhct test's live
display on a terminal, then prints the same report as before. With
--fix, repairs still run one at a time (package managers allow one
install at a time), and a language an earlier repair fixed isn't
repaired again. -
One password prompt, up front:
hct installandhct doctor --fix
name the languages whose install may need administrator rights and say
why, then ask for the password once (sudo -v) before any installer
runs, keeping it valid until they finish, instead of sudo prompting
halfway through. Nothing is asked as root, in CI or off a terminal. -
A project pins its hecatron:
"hecatron": "0.6"in
.hecatron/config.jsontakes any 0.6.x,"0.6.1"only that release
(">=0.6.0"is still a minimum). Another version warns on stderr and
failshct check(a dev build only warns), and the generated GitLab
pipeline installs the pinned version (#167). -
install.sh takes a series:
HECATRON_VERSION=v0.6installs the latest
0.6.x release (#167). -
Root needed less: OSV-Scanner, formatters' release builds (StyLua,
Air, Fourmolu, Ormolu), V, Goose, Elm's tools and bundler-audit now go
in hecatron's bin directory,~/.local/share/hecatron/bin, rather than
/usr/local/binor/opt, so installing them needs no password.hct
puts that directory first on PATH for everything it runs. On macOS only
Perl's and Dart's installers still ask for administrator rights. -
pythonfor builds that call it: where there is onlypython3,
Python's installer (and the generated Dockerfile) linkspythonto it in
~/.local/share/hecatron/python/bin, which every language's hooks have
on PATH, so a Rust or C++ build that runspython(rerun's, xgboost's)
finds it. Nothing outside hecatron's hooks sees the link, and apython
installed later replaces it (#206). -
pytest collects
Test*.pyfiles (antlr4's Python runtime) where no
test file has pytest's default names and the project sets no
python_files(#206). -
pytest leaves out the Python projects inside the one it tests
(pydantic-core in pydantic, ast-grep'scrates/pyo3), whose dependencies
the outer project's environment doesn't install, unless the project's
testpathsname them (#206). -
A
tsconfig.jsonbeside mostly JavaScript is a JavaScript package
(antlr4'sruntime/JavaScript, with its type declarations), in detection
and in finding a language's directory: a definition's newdetectYields
rule gives a hit to another language where its files outnumber the
language's own. Committed build output doesn't count (dist/,build/,
out/,lib/, theoutDirintsconfig.json, a.jsbeside its
.ts), so a TypeScript package that tracks its compiled JavaScript
stays TypeScript (#206). -
Libraries a Makefile build includes: the C, C++ and Objective-C
installers install the common libraries the sources of a build without
CMake#include(<zlib.h>,<openssl/ssl.h>,<sqlite3.h>...), as they
did for CMake'sfind_package()(CobolCraft) (#206). -
GoogleTest programs CTest doesn't know of run: a CMake project whose
build makes a GoogleTest program it never registers with CTest
(LightGBM'stestlightgbm) has that program run as its tests (#206). -
Zig versions side by side: a component whose
build.zig.zonsets
minimum_zig_version = "0.14.0"gets Zig 0.14 (the newest 0.14.x), not
the default 0.17: Zig breaks compatibility between minor releases.
hct installinstalls each version the components need next to the
default in~/.local/share/hecatron/toolchains/zig/, andhct testand
the other hooks run each component's own (hct env zig -c COMPONENT
shows it). Thezigon PATH stays the default. Older releases install
too (their downloads are named differently before 0.14.1). -
A current Erlang/OTP on Ubuntu: Erlang's installer installs
builds.hex.pm's OTP 29.1.2 (the defaultversionin its definition) into
~/.local/share/hecatron/toolchains/erlang, with its commands and
rebar3's release (3.27.3, checked against its SHA-256) in
~/.local/share/hecatron/bin, without root, instead of Ubuntu's own
Erlang: 24.04's OTP 25 is too old for current libraries (hackney) and
Gleam's standard library, which need OTP 26. Debian, which builds.hex.pm
has no builds for, still gets its distribution's Erlang and rebar3, and
macOS Homebrew's. The generated Dockerfile installs the same OTP and
rebar3. Gleam installs Erlang's OTP when theerlon PATH is older than
26, and its doctor reports one that is;hct uninstall erlangremoves
the OTP build and rebar3 it installed (#206). -
Current SWI-Prolog on Ubuntu:
hct install prologadds SWI-Prolog's
PPA (ppa:swi-prolog/stable, its key checked against its fingerprint)
and installs or upgrades SWI-Prolog from it, so packs that need 9.2 or
later (lsp_server) work; Debian and macOS keep their own.hct doctor
reports the PPA missing, andhct uninstall prologremoves it. An
install.jsoncan name such a repository for any language
(aptSources). -
Quicklisp for Common Lisp:
hct install lispinstalls Quicklisp into
~/.local/share/hecatron/quicklisp(no root; its quickstart pinned by
checksum), and the Common Lisp hooks fetch the dependencies of the
systems they build and test (their test systems' too) with it, so a
project such as cl-ppcre, whose tests need cl-unicode, tests without
setup.hct doctorreports Quicklisp missing;hct uninstall lisp
removes it. A Quicklisp the user's init file loads is used instead. -
JavaScript or TypeScript below: without a
package.jsonin a
component's directory, the Node.js hooks pick the packages below as
detection does, so a mostly-JavaScript package with atsconfig.jsonis
JavaScript's, not TypeScript's. -
Secret scanning:
hct auditlooks for committed credentials with
gitleaks, whichhct install secretsinstalls (8.30.1, pinned and
checked against its checksum, into~/.local/share/hecatron/bin, no
root).hct audit secretsruns only the scan, of the working tree less
what git ignores, or of every commit with--history. Findings show
the rule, file, line and gitleaks' fingerprint, never the secret, in
every output and in the SARIF report and job summary, and fail the
audit. A project's.gitleaks.tomland.gitleaksignoreapply, and
"audit": {"secrets": {"ignore": [...]}}leaves out paths such as test
fixtures."ci": {"secrets": true}generates a CI job that runs it
(#191). -
Docs in two sets: a user guide, with a
reference for every command and its options, the project's
configuration, hooks, toolchain versions and generated files, and a
contributor guide, with the architecture,
each module, how a command flows through them, the install engine,
testing and releasing.docs/design.mdis now the design record in
docs/contributing/design.md;docs/agents.mdstays where it was (#98). -
hct exec LANG [-c COMPONENT] -- CMD...runs a command with a
language's environment for a component, whathct env LANG -c COMPONENT
prints, without anevalfirst: useful where components use different
versions of one toolchain. Without-c, the component whose directory
holds the working directory, else the only one in the language. CMD runs
in the working directory and its exit status is hct's; a version not
installed is reported with thehct installto run (#225). Needs the
binary. -
A software bill of materials:
hct sbomwrites CycloneDX 1.6 JSON
(or SPDX 2.3 with--format spdx) for every component in every
language, from whathct licensesreads: each package's purl, its SPDX
license where known (never guessed), direct or transitive where the lock
file says, dev and test dependencies marked (--no-devleaves them out),
and the project's name, commit and a timestamp (SOURCE_DATE_EPOCHfor
reproducible output). To stdout or-o FILE; positional languages and
-c COMPONENTselect ashct licensesdoes. Packages from registries
deps.dev has no licenses for (pub.dev, Hex...) are listed without
licenses (#226). -
hct docsandhct bench: two new run commands.hct docsbuilds
each component's API documentation with its ecosystem's tool into the
tool's own directory (cargo doc --no-deps, Sphinx with a
docs/conf.pyor pdoc, Javadoc, Dokka, Scaladoc, DocFX, ExDoc, EDoc,
DocC, Haddock, odoc,dart doc, Documenter, TypeDoc, YARD or RDoc,
Doxygen with aDoxyfile...) and ends saying where (also in agent and
JSON output).hct benchruns benchmarks (cargo bench,go test -run '^$' -bench . ./..., pytest-benchmark, JMH, BenchmarkDotNet,
cabal bench, package-benchmark, a CMake bench target or Google
Benchmark programs...). Both run only tools a component already has;
a language or component with none says so and passes. A project's
docs.shorbench.shreplaces the default
(languages) (#227, #228).
hecatron 0.6.0
-
Detection: dotfiles aren't sources (a
.nvim.luano longer adds
Lua); Lua isn't detected from premake build scripts, nor R from an
Octave package'sDESCRIPTION. -
Default hooks find more layouts: PHP's test and deps hooks use the
nearestcomposer.jsonabove (a root one coveringlang/php, running
its PHPUnit on the directory'sphpunit.xml); Dart's test hook tests the
packages below with atest/(a pub workspace's root,pkgs/*); R's
tests each package below withtests/testthat/(R-package/), and its
deps hook installs aDESCRIPTION's missing dependencies from CRAN
(hct install rnow fetches them); Ruby compiles a gem's native
extension (rake compile) before testing; Racket links sibling packages
(each with itsinfo.rkt) beforeraco test; Elm runs
elm-explorations/test1.x on the last elm-test supporting it, with
Elm 0.19.1. -
Installs: PHP installs the extensions a
composer.jsonor its
dependencies require (ext-curl,ext-mongodb...) where packages
exist; opam installs the system packages dependencies need; Tcl installs
makefor TEA extensions' tests. -
Node.js workspaces built before their tests: when a package in the
workspace a test runs in needs building (a native addon or entry point
not built yet),hct test typescriptandhct test javascriptbuild
the workspace first (pnpm -r run build,npm run build --workspaces,
Yarn'sworkspaces foreach --topological run build), so a roottest
script that runs every package's tests, or a package importing another
one'sdist/, finds them built. -
.NET's
rollForward: latestMajoris a floor: aglobal.jsonasking
for SDK 8.0.100 or later now gets hecatron's default SDK (10), or the
newest of its own major when that is newer than the default, raised to
the newest major the component's projects target (net11.0needs SDK
11). Before, it read as SDK 8, which can't build a project targeting
net10.0. Definitions name such files in the newfloorFileskey. -
hct self: hecatron's own commands take a space, asrustup self
anduv selfdo:hct self update,hct self testandhct self build;hct selflists them.self-testandself-buildstill work. -
hct self update: updates an installation install.sh made to the
latest release (or--version X.Y.Z), its binary checked against the
release'sSHA256SUMSand the new version swapped in whole, so a
failure leaves the old one working; rewritesenvandenv.fishas
install.sh does, so new PATH entries (the toolchains' links) take
effect; refreshes the Agent Skillhct skill installput in place.--checkonly reports (exit 3: a newer release).
Once a day at most, a release binary looks for a newer release in the
background and says so in one line after a command, on a terminal in
human output only (never in CI, agent or JSON output, or with
HECATRON_NO_UPDATE_CHECK=1);hct statusreports it from that check.
Hecatron never updates itself. Needs the binary. -
Detection: a language whose build files are all more than three
levels below the directory the layout gives it (antlr4's Go module in
runtime/Go/antlr/v4, jsii's inpackages/@jsii/...) gets its own
implPathsentry instead, since the hooks look no deeper and failed with
"no go.mod here, below, or above it"; files undervendored/no longer
make a language. -
JVM and .NET builds in real projects: a Java, Kotlin, Scala or
Groovy directory inside a Gradle build that isn't one of its projects
(moshi/src/main/java) runs Gradle from the project it belongs to,
instead of failing as "not part of the build"; a Kotlin Multiplatform
build without atesttask runsjvmTest, elseallTests; a
multi-module Maven build tests through the package phase; Mill builds (build.mill) are tested;
the JVM gets a UTF-8 locale when none is set; Maven, Gradle and sbt are
installed for builds below the language's directory too. .NET tests roll
forward to the installed runtime (a test project targeting net8.0 runs on
.NET 10), and a directory with several projects and no solution tests
each test project. A Clojure project withdeps.ednbut no:test
alias tests with Leiningen when it hasproject.clj. A Gradle build's
toolchain JDK is found from its root and its version catalog
(libs.versions.jdk), so a source directory's build gets it installed,
and a build whosejavais a runtime withoutjavac(a JRE another
package pulled in) runs on the newest installed JDK. -
hct dockerfile: the generated Dockerfile gives curl a config file
(throughCURL_HOME) with the installers' retry policy plus
--retry-all-errors, so a transient download failure, such as a dropped
TLS connection to api.adoptium.net, is retried instead of failing the
build. It applies to everycurlin the build and in the image. -
Python tests in monorepos:
hct test pythonin a directory without
apyproject.toml,setup.pyorsetup.cfgnow tests the Python
projects in the directories below (py-polars/,python-package/),
each in its own environment, instead of running pytest over the whole
tree; with none below, pytest runs there on its own as before. -
Swift: a package that declares no test target (a C library's
Package.swiftfor SwiftPM users) passeshct testinstead of failing
on "no tests found". -
pnpm and Yarn for every language's hooks: Corepack's pnpm and Yarn
shims live in~/.cache/hecatron/corepack-shims, whichhct install
now creates when a package needs them and which is a staticpath
entry of JavaScript and TypeScript, so another language's build or
tests that runpnpm(a Rust test in a pnpm monorepo) find it. -
Bash is detected from a Bats suite: a definition's
detectTests
(Bash's*.bats) count intest/andtests/for a language not
otherwise found, placing it at the directory tested, so bats-core and
Bash-it are Bash projects. Bash's test hook leaves out.batsfiles
under afixtures/directory, which are test data. -
Fixed: a random "Unknown language", skipped dependencies or wrong doctor
result under load. The shell scripts checked list membership by piping
intogrep -q; when grep matched early, the writer was killed and, under
pipefail, the check said "not found". They now use a plain loop (#197). -
A Kotlin JVM target picks the JDK: a Gradle build whose Kotlin sets
jvmTarget(11 or later) and that sets no Java target or toolchain runs on
that JDK, whichhct installinstalls, instead of failing Kotlin's check
that Java and Kotlin target the same JVM. -
JDK 8 installs: a Gradle or Maven toolchain asking for JDK 8 gets
Temurin 8, whose releases are namedjdk8u412-b08. Before,hct install
failed with "No Temurin JDK release matches 8" and installed no other JDK
the build needed either. -
Native builds: C, C++ and Objective-C build with CMake or Meson in
_build/when the project tracks files inbuild/(configuring into it
mixed build output with its sources, andhct cleandeleted them); a CMake
project that never sets up a test passeshct testafter building instead
of failing on "No tests were found"; installers install Meson and Ninja
where a component builds with Meson, pkg-config (which Makefiles call)
with C and C++, and the libraries PCRE2 and cJSON where a CMake build
looks for them. -
Native tests: a CMake project that calls
enable_testing()in a
subdirectory rather than at its top level (Zstandard) runs its CTest tests
from that subdirectory's build directory instead of failing on "No tests
were found"; a Makefile project builds its default target beforemake checkormake test(Redis's tests run programs onlymakebuilds); and
a Meson build directory a failed setup left behind is set up again. -
Rust's system dependencies:
hct install rustinstalls what a
component's crates build with, read from its Cargo.lock files: the
libraries common-syscrates link (OpenSSL, GLib, GTK and WebKitGTK,
D-Bus, ALSA...), Python's headers for PyO3, and the tools build
dependencies run (CMake for thecmakecrate and zlib-ng,protocfor
prost-build unless protoc is vendored, Cap'n Proto, libclang for bindgen),
and therust-srccomponent where ui_test's UI tests need it. -
Python test dependencies in dependency groups:
hct test python
andhct deps pythonnow install every PEP 735 dependency group
(uv run --all-groups,uv sync --all-groups), not onlydev, so
test dependencies kept in atestortestsgroup are there. -
Meson versions: where a
meson.buildasks for a newer Meson
(project(..., meson_version: '>=1.8.3')) than the one installed,hct doctorreports it andhct install c(orcpp,objectivec) installs
the current Meson from PyPI as a uv tool on Linux, whose distribution
Meson lags (Ubuntu 24.04's 1.3 can't build NumPy or SciPy). -
Rust's system dependencies:
hct install rustreads every Cargo.lock
in the project outside the trees detection skips (examples, tests,
vendored code...), not only those of the Rust components, so a crate
another language's build compiles (a Java binding's Maven build running
cargo) gets what it needs too, such asprotoc. -
Rust tests:
hct test rustrunscargo testrather thancargo test --all-targets: Cargo's default targets, now with doctests, and without
running benchmarks as tests (a nightly-only benchmark broke stable builds,
and others fail or run long in test mode). -
A fresh install from
install.shno longer prints git's "is not a commit" warning and its detached HEAD advice. -
hct self uninstallremoves hecatron:~/.hecatron, its lines in
your shell's startup files and fish's conf.d, and its Agent Skill. It
asks first (`--ye...
hecatron 0.5.0
- Leave files out of formatting:
"format": {"exclude": [...]}lists
pathshct formatandhct lint's format check skip (a fixture
misformatted on purpose, generated code), where hecatron gives the
formatter its files. - A missing linter says so:
hct lintwith a language's linter not
installed (golangci-lint, say) reports the toolchain missing (exit 3), and
"command not found" leads the failure's excerpt. initdetects better: a build file at the root keeps its language
there unless the directory the layout names for it holds the language's
code (in a repository withgo.modat the root and alanguages/go/of
other files, Go lives at the root, not inlanguages/go/), and files
underhooks/are tooling, not a language (a Tcl hook script no longer
adds Tcl). Applies tohecatron initand to a repository read without a
config.hct licenses: every dependency's license, in every language and
component, from the lock files (OSV-Scanner with deps.dev for npm, PyPI,
Go, crates.io, Maven, NuGet and RubyGems;composer.lockfor PHP):
rolled up per SPDX license, each package with its version, direct or
transitive and dev marked, unknown licenses apart, and what isn't covered
said so. A"licenses"policy in the config (allow,deny,ignore
with a reason and an expiry,includeDev) fails the command on a package
it refuses;hct checkvalidates it.--output human|agent|json. Needs
the binary (#132).- CI jobs get a time limit:
hecatron workflowsadds
timeout-minutes: 60to each generated GitHub job that sets none, and
timeout: 60mto each GitLab job, so a hung job stops after an hour
instead of GitHub's 6. Set it for the project or
per language in.hecatron/config.json
("ci": {"timeoutMinutes": 30, "languages": {"pascal": 120}}); a
template's owntimeout-minutesstands. Every generated workflow
changes once: runhecatron workflowsafter upgrading, or
hecatron checkreports them out of date. - An Agent Skill for hecatron: AI agents that read Agent Skills learn
hecatron without the docs: first steps, the test-lint-format loop, exit
statuses, asking before installs, and what to do when something fails.
In Claude Code:/plugin install hecatron --marketplace dnf/hecatron; in
claude.ai, upload the release'shecatron-skill.zip. - More of hecatron runs in the binary, and faster: toolchain version
resolution and each language's environment (hct testacross 8
languages × 3 components: 0.28 s of setup to 0.07 s),initand the
other project commands (check,add,removeunder 10 ms), and
doctor's checks (1.6 s to 0.6 s for 8 languages). Same output as
before, checked byte for byte. - macOS fixes: Ruby installs when Homebrew's ruby-build lags (it uses
ruby-build's repository, as on Linux); Fortran's doctor checks gfortran,
so--fixinstalls it; Scala's smoke test uses Scala CLI's flag; Ada sets
SDKROOTfromxcrun, so GNAT links when the Command Line Tools are
newer than the selected Xcode. - Elixir on Linux installs Erlang's development files, so dependencies
that compile Erlang sources (leex, yecc) build. - OCaml installs every opam package's dependencies, for a repository
with several.opamfiles too. hct skill install: puts hecatron's Agent Skill where an agent finds
it:~/.claude/skills(the default), the repository's.claude/skills
(--project, to commit) or another agent's skills folder (--dir).
Reinstalling replaces hecatron's own copy and nothing else.hct status: what to know first in a repository, in one command:
hecatron's version, whether the config is saved or detected, and for each
language its directories, the toolchain version it resolves to and why,
whether that toolchain is installed, the last run of each action, and its
uncommitted changes; then the commands worth running next, marking those
that install something.--output human|agent|json, chosen as for
hct test. Needs the binary that releases ship (#121).hct advisor: wherehct doctorkeeps the machine working, the
advisor reads the project and recommends what each language's ecosystem
considers good practice, each finding with what to change, why, and a
link to the ecosystem's own documentation: lock files committed (with each
ecosystem's nuance: Cargo binaries, Composer projects, Dart and Ruby
applications) andpackage-lock.jsonstill matchingpackage.json,
toolchains pinned in the ecosystem's own file and not past end of life
(from the release tables hecatron ships), a formatter and linter
configured, Gradle's build and configuration caches,npm ciin CI, CI
covering every language, GitHub Actions not pinned to a branch, and audit
acceptances past their date. Rules are data in each language definition
(advice.json); rules for 23 definitions so far. Reads files only;
--fixadds missing pin files at the version in use;--fail-on recommended|suggestionfor CI;"advisor": {"ignore": [...]}in config,
validated byhct check. Needs the binary (#105).- GitLab CI and caching in generated CI:
hct workflowswrites a
.gitlab-ci.ymlwith a job per language (install, test and lint through
hct) whengenerated.ciis"gitlab"(or["github", "gitlab"], or
with--ci gitlab), andhct checkfails when it falls behind. With
"ci": {"cache": true}the generated GitHub workflows and GitLab jobs
cache each language's toolchain (keyed on its pin files and hecatron's
version) and dependencies (keyed on its lock files); each definition
lists the directories in its newcachekey. Off by default, so existing
workflows don't change. Needs the binary (#102). - Dependabot and Renovate configuration: with
generated.dependabot
(orgenerated.renovate),hct workflowswrites.github/dependabot.yml
with anupdatesentry for each package ecosystem and component
directory (renovate.jsonwith the managers and paths), merging the
project'sdependabot(orrenovate) settings, andhct checkfails
when a language or component was added without regenerating it. Each
definition names its ecosystems in newdependabotandrenovatekeys.
Needs the binary (#103).
hecatron 0.4.0
-
A fast binary: installing a release also installs the Go core for
your machine (macOS and Linux, x86_64 and arm64), checksum-verified, and
hctruns it:--versionandlistanswer in about a millisecond
(80 ms before, on a 51-language project), and help,list,envand the
run commands no longer depend on the system's bash (macOS's 3.2). The
run commands take--output human|agent|json|ci(orHECATRON_OUTPUT):
agent output is a summary line and only the failures with their key
error lines; exit status 3 means a toolchain is missing. The rest still
runs through the scripts.HECATRON_NO_GO=1keeps to the scripts. -
Fixes on macOS's bash 3.2: every run command's help and
hct doctorno longer fail with "unbound variable" in a repository with no
languages (which the binary avoids altogether). -
.NET runs the SDK it should: a version the project's config names
runs exactly that SDK, each version in its own view of/opt/dotnet; a
version a GitHub workflow names is installed but, assetup-dotnet
does, the newest SDK runs unless aglobal.jsonpicks one, and
hecatron doctornow says which. Downloadingdotnet-install.sh
retries a dropped connection. -
Installing everything fetches dependencies last: with no languages
named,hecatron installinstalls every toolchain first, then fetches
dependencies once, so one language's fetch that needs another's
toolchain (apreparescript running Ruby) no longer fails. -
Perl runs a project's own Makefile test target, which may set what
its tests need (AUTHOR_TESTING): cucumber/tag-expressions runs 169
tests through it, where hecatron'sproveran 68. -
hecatron initskips a package with no test script for a JavaScript
or TypeScript implementation (a JSON-schema package beside the real
one), saying why. -
A generated image fails when its PowerShell modules didn't install,
instead of shipping without Pester. -
Instructions for AI agents:
hct initwrites a short section to
AGENTS.md(and has an existingCLAUDE.mdload it);--no-agentsskips
it.hct help agentsprints a compact reference written for agents. -
No setup step: in a git repository without
.hecatron/config.json,
every command works on whathct initwould detect, remembered inside
.git/until the tracked files change; nothing is written to the working
tree.hct initsaves it, to commit and adjust. -
hct lintnow fails on unformatted code. It runs each language's
format check (below) after its linter, where the linter doesn't check
formatting already, so a project whose code isn't formatted the way its
formatter says will seelintfail. Runhct formatonce, or turn the
check off in.hecatron/config.jsonwith"lint": {"format": false}(or
{"format": {"haskell": false}}for one language). See
docs/format.md. -
hecatron outdated: lists each component's direct dependencies that
have a newer version, from each ecosystem's own report (npm, pnpm, Yarn,
uv,go list -u -m, Cargo's dry-run update, Bundler, Composer, the .NET
SDK, Hex, pub, Maven's versions plugin, Gradle's Versions plugin where a
build applies it, cabal), with current, wanted and latest versions.
Report-only unless--fail;--jsonprints one array. See
docs/outdated.md. -
hecatron deps: fetches every component's dependencies with its
ecosystem's tool, held to the lock file where there is one: npm, pnpm,
Yarn or Bun's frozen install,uv sync --locked, Bundler frozen,
composer install,dotnet restore --locked-mode,go mod download,
cargo fetch --locked,mix deps.get,pub get --enforce-lockfile,
swift package resolve, Maven'sdependency:go-offline, Gradle, sbt,
Stack or cabal, and more. Languages without a dependency manager say so.
The test, lint and build hooks that install missing dependencies, and the
installers, now do it the same way, so pnpm, Yarn, Bun, Bundler, Dart
and Shards installs no longer change a lock file: one that's out of
date fails, asnpm cialready did. -
hecatron format: rewrites every component's code with its
language's formatter, using the project's own configuration: gofmt,
rustfmt, Ruff (or Black),dotnet format,mix format,dart format,
zig fmt, swift-format, elm-format..., and where the project configures
one, Prettier or Biome, RuboCop or Standard, PHP CS Fixer or Pint,
Spotless, ktlint or scalafmt through the build, clang-format, StyLua,
perltidy, Fourmolu or Ormolu, Air, cljfmt, erlfmt, JuliaFormatter and
ocamlformat.--checkchanges nothing and lists the files it would
change. The installers add the formatters a toolchain doesn't ship, at
pinned versions. -
hecatron audit: checks every component's dependencies for known
vulnerabilities with each ecosystem's auditor (npm, pnpm, Yarn, Bun,
pip-audit, govulncheck, bundler-audit, Composer, the .NET SDK, and
OSV-Scanner for the rest), fails at--fail-on(default high), and takes
accepted risks with a reason and an expiry date. See
docs/audit.md. -
Old checkouts get the toolchains of their day: when a repository's last
commit is over a year old, each language's version becomes the release
that was current then, unless the project pins an exact one. A 2021
checkout gets the Node.js and JDK it was tested with instead of today's,
which can no longer build or run it.HECATRON_AS_OFsets the date or
turns this off;hecatron doctorsays when it applies.
A weekly workflow keeps the release dates this uses current. -
CI workflows and Maven builds say how new a toolchain may be: with no
pin file, the versions a project's GitHub Actions setup steps use, and a
Maven build's Java source level, hold the default back. -
JavaScript components choose their Node.js version like TypeScript's.
-
Python follows the versions a project supports: with no pin file, a
component whose classifiers stop at an older Python than the default
(Avro's at 3.14) gets that line's newest release, since compiled
dependencies often lag a new Python.hecatron doctorsays when. -
elm-review is on PATH: global npm commands Elm installs are all linked
onto PATH, now that Node.js comes from its official builds. -
CMake builds run fewer compile jobs at once: one per CPU (or CPU
quota) but at most one per 2 GB of memory, so a large C++ project no
longer has its compilers killed for memory.CMAKE_BUILD_PARALLEL_LEVEL
still decides when set. -
A CMake subdirectory is built as part of its project: a component
whoseCMakeLists.txtcalls noproject()(one a parent's
add_subdirectory()pulls in) is configured from the nearest directory
above it that does, and its own tests run. -
CMake test options are the project's own: the test hook no longer
turns on a fetched dependency's test options, nor valgrind or
large-memory ones, and an option whose setup is missing is left off
instead of failing the configure. A project that registers no CTest
tests and runs them from acheckorteststarget gets that target. -
C and C++ installers find more of what a CMake build needs:
pkg_check_modules()andpkg_search_module()lookups, headers looked
for by hand (gtest/gtest.h), filesinclude()d from outside the
component, the enclosing project's own lookups, and more libraries (GLib,
RE2, ICU, Jansson, Snappy, zstd, LZ4, libevent, flex, bison...). -
Perl installs Module::Install and the extensions a
Makefile.PL
written with it names (configure_requires), which a checkout doesn't
bundle, and installs the CPAN dependencies aMakefile.PLorBuild.PL
declares, as it did acpanfile's. -
Dart installs Flutter (3.47.8) where a component is a Flutter package,
and fetches its packages withflutter pub get; the doctor reports it
missing. The test hook says so when a package has notest/directory. -
JVM builds get the JDKs they ask for: the JVM installers install the
JDKs a Gradle or Maven toolchain names, and a build whose Gradle wrapper
(before 9.1) or Kotlin compiler (before 2.3) can't run on the default
JDK runs on the newest LTS JDK it supports, which they install too. -
Python installs a C compiler on Linux, for dependencies that build a
C extension from source. -
Default hooks find a build that isn't in the language's directory:
above it in the project (pulumi'ssdk/goin the modulesdk/go.mod, a
rootPackage.swift, CMake project or Gradle build), or in directories
below it, run in each (several crates underrust/, a solution in
csharp/src/), or a root Makefile's target named for the directory
(check_cxx). -
Node.js uses the project's package manager: the hooks and installers
run pnpm, Yarn or Bun whenpackageManageror the lockfile says so (pnpm
and Yarn through Corepack), so a pnpm or Yarn workspace installs instead of
failing onworkspace:*. -
Toolchain versions for .NET (C#, F#, VB.NET): a pinned default SDK
(10.0.403 LTS, the 10.0.4xx feature band), or the one a component's
global.json asks for, installed side by side with Microsoft's
dotnet-install.sh into/opt/dotnet(per-user on macOS) rather than the
distribution's package, which holds one feature band; the runtimes older
target frameworks need go there too. A newer band's analyzers no longer
fail with CS9057 on Ubuntu's 10.0.1xx compiler. -
Toolchain versions for PHP: a pinned default (PHP 8.5), or the line a
component's.php-version, composer.jsonconfig.platform.php,
.tool-versionsor mise asks for, installed side by side from Ondřej
Surý's packages (his PPA on Ubuntu, packages.sury.org on De...
hecatron 0.3.0
- An installer:
curl -fsSL …/install.sh | shinstalls the latest
release into~/.hecatronand adds it to bash, zsh and fish startup files;
sourced from a clone, it also sets up the current shell. Rerun to update.
It first installs what hecatron needs if it's missing: the Command Line
Tools on macOS, and git, Python 3 and perl on Linux (apt, dnf or zypper). - Downloads retry transient failures (timeouts, dropped connections,
429 and 5xx answers, stalled transfers) in installers, hooks, apt and the
installer's git operations; a 404 isn't retried. - A smoke test for every language: each builds and runs a small program
with the freshly installed toolchain, and checks its output. - Groovy, Scala and Kotlin install their own compilers (Groovy 5.1.3,
Scala 3.3.8 LTS, Kotlin 2.4.21), not only the JDK. - Default hooks for every language. A conventional project needs no hook
files: each language's test, lint, build and clean commands are chosen from
the build files present (#36). - New languages: JavaScript, Objective-C and Haxe, for 54 in all (#36).
- Toolchain versions for Python, Go, Rust, Node.js and Java: a pinned
default (Python 3.15.1, Go 1.27.3, Rust 1.99.1, Node.js 24.22.1 LTS,
Temurin JDK 25.0.5 LTS), or the version a component's own pin file asks
for, installed side by side (#19, #27, #28). A component on the default
accepts any installed version on the default's release line (#31). The JDK
is Temurin rather than the distribution's OpenJDK 17, and Node.js comes from
nodejs.org rather than NodeSource. hct initfinds real layouts: directory aliases such asdotnet/,
javascript/andlib/py, a directory per language where one template
doesn't fit, and examples, tests and vendored code skipped (#33, #34, #35,
#37).- Generated Dockerfiles install every version the components use (#30).
hct self-testruns ShellCheck and the unit tests; hecatron checks at
startup for bash 3.2+ and Python 3.9+, and no longer asks for bash 4 (#38).- Hecatron or ad hoc?, a ten-language
library maintained with hecatron and without it (#15). - Fixes for macOS (Python, Mojo, D, Lua, Pharo, Go) and Debian
(PowerShell) from the first full coverage run (#32), Ruby gems with native
extensions (#25), and Wren on arm64 (#21).