Changelog¶
Unreleased¶
Added¶
zoryn task download -b/--builder PATTERN: fill the hasher repo (RPMS.hasher) of the selected builder(s) instead of the default local hasher — local builders viacp, remote via rsync over SSH, each for its own architecture plus noarch (builders whose arch has neither its own nor noarch RPMs are skipped, and the upload is verified by name to have landed); builder patterns (@all,@host:HOST, ranges) are supported, an explicit-B/--branchrestricts builder selection, and-o/--archare rejected in this mode (#159)zoryn task download --clean: remove all*.rpmfrom the destination before downloading — the hasher repo directory also holds earlier build results, so this is destructive by design; zero-RPM downloads fail instead of succeeding silently (#159)zoryn submit --tag-name TAG: explicit gear tag name for specs whoseVersion/Releaseevaluate only at build time (%(...),%get_version pkg— e.g. javazi); it doubles as the tag message on creation and recreation, and without the flag such specs fail early with a hint instead of passing raw macros togear-create-tagzoryn upcompares geardiffpatches before and after the update, including compressed directives (#157). A new non-empty patch stops the update. Changed or removed existing patches require terminal confirmation outside flow; flow up steps log and accept them without questions, including after continuation. Messages distinguish a new non-empty patch from changes to a cumulative patch against a fixed upstream tag. Unchanged non-empty patches produce a reminder. A separatepatch-checkstage preserves the original snapshot across--continuewithout rerunning successful hooks. Updates started byflowortask rebuild --uppersist their caller before the first stage and hand completed results back after manual continuation, including hard interruptions and same-process recovery flows. Task rebuild results are bound to their task and package and survive commit, tag, push, or task submission failures; clean retries do not repeat the commit. Explicit--stage=patch-checkand build stage selectors require the saved snapshot and cannot bypass a refused check.--restartrefuses to replace an interrupted update; continue or abort it first. Detect selectors also refuse, unless an explicit--reset-to-gearreset succeeds. Early states without a selected version and snapshot cannot enter build selectors. Recovery-flow steps cannot consume their parent's update result. Task retries use the rules-selected spec, refuse a different current version, and verify already-submitted tag object IDs. Patch capture follows gear's active rules in the tracked tree, includinggear.rules, and supports quoted paths and directives without a space after the colon.zoryn devenv:run/run_userare accepted in[devenv]config as aliases ofbuild/build_user, andbuild/build_userinfeature.tomlas aliases ofrun/run_user
Changed¶
zoryn up --reset-to-gearnow switches to and resetssisyphusby default, regardless of the current checkout. An explicit-B/--branchselects the reset branch instead. The reset rejects comma-separated lists and invalid Git branch names before changing state, fetches only branch refs, and discards interrupted pipeline metadata without rolling back another branch. Fetch/checkout failures preserve recovery state (#156)zoryn devenv: thepifeature now uses the official managed installer with pinned dependencies instead of global npm installation. The installation stays outside the host-mounted settings directory, withpilinked into~/.local/bin; the feature now mounts the entire host~/.pidirectory read-write, not only~/.pi/agent- Default builder commands: local and remote builders now share one set of
hshtemplates, a remote build or rebuild only appends the uploaded tarball path. The remotebuilddefault gained/dev/kvmin its mountpoints, matching the local default and the mountpoint check that already required it; a test guards against the two drifting apart again (#149)
Fixed¶
zoryn flow --continuenow resumes an interrupted update owned by its current step instead of refusing its saved checkpoint. Completed stages are not repeated; unrelated updates and saved build stages are preserved and require separate continuation.zoryn flow --abortnow also aborts an interrupted update owned by its current step, or discards its completed result without rollback. Unrelated updates are left untouched. Failed rollback preserves both flow and update state for another attempt.zoryn task delete/task delsub: a successful subtask deletion now remembers the task ID, so a followingtask addwithout an ID targets that task rather than a previously remembered one; failed deletions leave the remembered task unchanged (#160)- Tag merges (
zoryn up,zoryn flow): the merge commit message no longer embeds the tag annotation (for signed tags, its PGP signature and gpg verification output); the message is now just theMerge tag '...' into <branch>line task test-rebuild --mkrepo: task packages are placed in the merged index by source rpm and filename, as in the committed branch, instead of after all branch packages. Before, apt could prefer a task package over a branch provider of the same capability that wins once the task is committedtask test-rebuild --mkrepo: packages that girar files outsideclassic(*-gostcrypto,*-debuginfo,*-checkinstall) no longer go into the merged index. Before,gnupg2-gostcryptodisplacedgnupg2and failed builds that girar builds fine with the same task- Development:
dune fmtno longer rejects unattached documentation comments in upstream tag selection and devenv feature contributions; each comment is now attached to its intended declaration zoryn up --continuenow honors the current--no-sandboxand--sandbox=RUNNERflags for hooks. Without an explicit runner it keeps the saved selection, but never reuses a previous run's permission to execute hooks without isolation (#155)- Shell completion in bash, zsh and fish: options after
zoryn submit --skip-checknow complete normally instead of being treated as check names;buildandupstill complete check names, or options when the next word starts with-(#152) zoryn cloneon a package whose remote HEAD points at a ref that no longer exists: git clones such a repository with exit code 0 but an empty working tree ("remote HEAD refers to nonexistent ref"); zoryn now detects this and checks out the newestold/<branch>-tasksnapshot (falling back to any other remote branch) as the local branch (#150)zoryn build --rebuildon a local builder: the defaulthsh-rebuildcommand now passes the builder'shasher_diras workdir, so it reuses that builder's chroot instead of looking in~/hasher, failing with "cannot find chroot" and falling back to a fullhshbuild that recreated the chroot (#149)zoryn build --rebuild: the fallback full build after a chroot error appends to the build log instead of truncating it, so the failed rebuild's own error is kepttask test-rebuild,task rebuild,task refresh: these no longer stop on a task that RDB still shows asNEW. The task's state, branch, owner and subtask list now come from the task itself (the Tasks API), which RDB's snapshot of a task can lag by hourstask rebuild,task test-rebuild: a failed RDB query for the dependent packages is now an error instead of an empty list, so a run no longer reports "no dependent packages" — and, intask test-rebuild, saves completed stats — when the query never answered
0.53.0 - 2026-09-13¶
Added¶
zoryn flow: a flow with asubmitorbatchstep asks for the tag signing key's passphrase before the first step (a no-op sign with the program and keygit tag -swould use), so pinentry appears while the terminal is still watched instead of after the build; skipped whengpg.formatissshzoryn agent mcp install --token TOKEN: stores anAuthorization: Bearer TOKENheader for the server. Unlike--api-keyit needs noapi-key-headerin the catalog, so it works for any server, including a one-off--urlone;--token ''removes a stored tokenzoryn buildand the build stage ofzoryn up: inside a devenv without thehasherfeature the build stops up front with thegear-rpm -ba --commitcommand instead of heading for the host's builders, which are visible through the mounted config but unreachable from the containerzoryn devenv: the bundled Agent Skill directory is bind-mounted read-only at its host path, so the pointer skill installed byzoryn agent skill installresolves inside the container; the mount is part of the container key, so existing containers are recreated once- Agent Skill:
references/repair.md— the unattended build-repair workflow (what to read, what may change, verifying withgear-rpm, when to stop); thezoryn flowagentstep's built-in prompt now points at it instead of carrying its own instructions
Changed¶
Agent Skill: building inside a devenv with
gear-rpm, notzoryn build/hasher, is now a rule at the top ofSKILL.mdand inreferences/devenv.md, with the reason (BuildRequires installed, host builders out of reach), instead of a soft preference at the end; the pointer skill's fallback says the same in two lineszoryn devenv: theclaudeandGLM-claude-codefeatures take aninstalleroption —native(default, the officialinstall.sh) ornpm(@anthropic-ai/claude-codefrom the npm registry, for networks where the native download is slow)zoryn devenv --outbound-interface IFACE/-I: one-shot override of[devenv].outbound_interface;-I ''unsets it
Fixed¶
zoryn up --continueafter a conflicted merge concluded by hand (git merge --continue): the merge stage now records that the merge happened in this run, so.gear/merge-up.d/hooks fire instead of being skipped as "already merged"zoryn gen version-up: a tag with both a prefix and a suffix (v1.5.0-stablefor version1.5.0) produced a pattern that matched nothing, sozoryn upthen failed with "Cannot find any tags for current version". The suffix is now embedded in the pattern as a literal (v{major:+}.{minor:+}.{patch:+}-stable) with a template that drops it, the same way a prefix already was; thestrip-suffixkey, which the generator wrote but nothing ever read, is no longer emitted (--updateremoves a stale one). Closes #145zoryn devenv(podman): changing[devenv].outbound_interface(or--outbound-interface) no longer rebuilds the image. The interface only selects the network the build runs over, not what it installs, yet it was part of the image tag, so flipping it paid a fullpodman buildfor an identical Containerfile. It now stays in the container key only (likemounts): the container is recreated from the cached image- parallel builds: aborting the remaining builders (
should_abort, Ctrl+C) could hang — a kill racing a just-forked child was lost because its process group did not exist yet; it now falls back to the child's pid task manage: the deps screen now lists a dependency whose task cannot be fetched (typically deleted withtask rm) as a dim placeholder row instead of dropping it, sodcan remove it; previously the screen showed only "fetch failed" with no way to unblock the tasktask test-rebuild: a run with no dependent packages now exits right after the RDB query, before the merged repo is generated (--mkrepo) and beforeRPMS.hasheris wiped; the--mkrepoconfiguration is still validated firsttask test-rebuild: an invalid--local-rpmspath is now rejected with the other flag checks, not only when the run reaches the RPM stagetask test-rebuild: a no-op run now saves an empty, completedrebuild-stats.jsonsnapshot before exiting
0.52.0 - 2026-09-01¶
Fixed¶
zoryn devenv: shell completion for--feature/-Fandzoryn devenv feature addnow lists local features from~/.config/zoryn/devenv/features/(both the flat<id>.tomland the<id>/feature.tomllayout), not only the bundled oneszoryn devenv:SHELL=/bin/bashis now exported into the environment (podman--envat create, bwrap--setenvat enter) — bash self-assigns$SHELLfrom passwd but does not export it, so tools reading the environment (terminal multiplexers, herdr) saw noSHELLand fell back to/bin/sh; a configured[devenv] enventry can still override it. Existing podman containers pick the fix up on the next recreation (--rebuild, or any config change that recreates)
Added¶
zoryn devenv(podman): the last resolvedBuildRequiresset is memoised per project+branch+arch (~/.cache/zoryn/devenv/buildreqs/) and baked into the derived image as a best-effort warm-up layer, so a container recreated for a reason outside the cache key (anenv/ports/dns/timezonechange, a new SSH agent socket,--rebuild) starts with its build dependencies already present and the in-container resolution installs only the delta — instead of re-downloading every build dependency. The layer is|| trueand sits last, so a stale memo can never fail the build and a memo-less image shares every earlier layer; the memo digest is folded into the image tag, never the container namezoryn devenv: a local feature definition can now be a flat~/.config/zoryn/devenv/features/<id>.tomlfile, alongside the existing<id>/feature.tomldirectory form (which wins when both exist); the unknown-feature error names both expected pathszoryn devenv:{devenv},{project},{branch}and{profile}placeholders in mount specs — configmounts,--mount-bindand featuremountsalike — so one entry gives every environment its own host directory (mounts = ["~/.config/herdr/sessions/{devenv}"]); the same placeholders expand in[devenv] envand featureenvvalues, so a tool inside the container can learn its per-environment directory.{devenv}is the container name fromdevenv list(bwrap substitutes thedevenv-<project>hostname), expanded late so the cache key keeps the literal token; the rest expand before the existence check and the key;{profile}readsdefaultwhen no profile is active, unknown{tokens}stay as writtenzoryn devenv:[devenv] envconfig key — an array ofNAME=valueentries set in the environment's shell (env = ["GOFLAGS=-mod=vendor"]). podman passes them as--envat container creation (the enter shell inherits them; a changed list recreates the container in place via the recordedzoryn.envlabel), bwrap as--setenvat enter time. The key concatenates global~/.zorynandprojects.dlayers, later entries winning per name; machine config only, never.gear/devenv. Features may declareenvinfeature.tomltoo (validated at parse,${NAME}option substitution as inmounts), applied before the user's configzoryn flow: theagentstep takes typed option keys —clioverrides[agent] clifor one step, andprompt(templated like acmd) replaces the built-in repair prompt, so a flow can point the agent at any job, not only fixing the newest build log; a step with its own prompt no longer requires a build log to exist. Like every typed key, both are also implicitzoryn flow <NAME>flags (--prompt '...',--cli codex). The[agent] flow_steps = truegate still applies
0.51.0 - 2026-08-29¶
Fixed¶
zoryn up/zoryn check version: an upstream tag whose commits were already fetched no longer hides a new release behind "up to date" — git auto-follows tags only for objects downloaded in the same fetch, so such a tag never arrived and the upstream-branch filter dropped the new version as "not merged". The staleness probe now also compares the remote's tag list and the sync fetch grabs tags explicitly (git fetch --tags)zoryn up: hooks from.gear/up.d/ran on every invocation, including an "already up to date" run that found no newer version — re-firing hooks that already ran when the package was actually updated. The stage is now skipped unless this run picked up a new version, mirroring the.gear/merge-up.d/gate;--stage up-hooks/--from up-hooksremain the explicit way to run the hooks on demandzoryn devenv(podman): clients connecting to the container from an address inside a[devenv.lan]allowprefix were silently blackholed — the sidecar's destination route captured the replies of connections that had entered through the primary interface, and the pasta sidecar, a socket-level relay, drops mid-stream packets of flows it did not initiate; the client's connection would hang on the first data segment and die with a RST after ~19 s, while outbound connections to the same subnet worked fine (withallow = ["10.0.0.0/8"]this broke, for example, every office-VPN visitor of a service published from the devenv). The sidecar's routes now live in a dedicated routing table entered through a pair of policy rules that split traffic by source: fresh outbound connections enter the table, replies sourced from the container's primary address bypass it. Running containers migrate on the nextzoryn devenv— the reconcile moves the routes and removes the stale main-table ones, no recreate needed. Two consequences of the source-based split are worth knowing: a UDP service bound to the wildcard address still replies over the LAN path (its replies have no source at routing time), and an allow-listed prefix now wins over the container's own connected subnet, which the main-table route used to keep for itself
Added¶
zoryn devenv(podman): entering an environment now warns when a bind-mounted file was replaced on the host since the container was created — a file mount is pinned to its inode, and a host tool rewriting the file via tmp+rename (how most configs are saved,~/.claude.jsonincluded) silently leaves the container on the old version. The inodes of file mounts are recorded in azoryn.mount_inodescontainer label at creation and compared on every enter; a mismatch is reported right before the shell opens, recommendingzoryn devenv --rebuildzoryn agent mcp: built-inbugzillaserver — the ALT Linux Bugzilla MCP endpoint (https://bugzilla.altlinux.org/mcp.cgi) — and a newinstall --api-key KEYoption that stores the key as an HTTP header in the agent config (header name from the catalog'sapi-key-header; bugzilla's isX-Bugzilla-Api-Key). Without a key the server answers anonymously; reinstalling without--api-keykeeps a previously configured key, and an empty--api-key ''removes it. Local~/.config/zoryn/mcp/<name>.tomldefinitions may setapi-key-headertoozoryn flow: first-classtask-runstep —{ run = "task-run", commit = true }starts the created task's build viazoryn task run, with typed keystask(default{task}),commitandmessagezoryn flow: first-classtask-approvestep —{ run = "task-approve", message = "sign" }runszoryn task approveon the created task;subtaskdefaults toall,messagebecomes the-mcommentzoryn flow: first-classtask-addstep —{ run = "task-add", args = ["rebuild"], dependent-on = "kernel-image-for-vm" }runszoryn task addwith typed, CLI-overridable keys (task,dependent-on);taskdefaults to{task}, the task thesubmitstep created, and all values are templatedzoryn flow:-Bis accepted as the short form of the implicit--branchstep flag, matching the rest of zoryn; bash completion now offers the selected flow's step flags and completes branch names after-B/--branch(zsh already completed branches). An ambiguous flag now lists the concrete qualified forms (--up.branch or --submit.branch) and suggests distinctnamekeys when steps of the same type collidezoryn devenv packages: print the packages azoryn devenvrun would install, without preparing an environment — the sorted unique union of the spec's staticBuildRequires(norpmbuildexpansion), configured[devenv]packages (~/.zoryn,.gear/devenv,projects.d), and packages from the selected features.--verbosegroups the list by source;--profile/--backend/--feature/--no-build-depshave the same meaning as onzoryn devenvzoryn up --no-build: run the update (version bump, spec, tags, hooks) without building — useful when a later step must run before the buildzoryn up --switch-to-upstream-git --force: re-run the migration on a repository that already uses the upstream git scheme — the re-merge overlays the upstream tree again, repairing a previously botched merge; without--forcethe command still refuses as beforezoryn task run/add/approve(anddisapprove) accept a comma-separated list of task ids (123456,123457), applying the operation to each in order and stopping at the first failure — mirrorssubmit -B[sandbox] outbound_interface(~/.zorynorprojects.d/<project>.toml): route all hook traffic through one host interface, by the same pasta mechanism as devenv'soutbound_interface— the hook's bwrap sandbox runs inside apasta --config-netnetwork namespace whose outbound sockets are bound to the interface. bwrap/hybrid runners only; requires thepasstpackage (a missing pasta fails the hook rather than silently keeping the host network)zoryn flow: unattended package-update workflows with custom bash/ssh/devenv steps, on-failure recovery flows and AI-agent repair (experimental). A flow is a[flows.<name>]list of steps in the machine config (~/.zorynor~/.config/zoryn/projects.d/<project>.toml— never the package repo, which may only select one via[flow] namein.gear/version-up), each step beingup,build,submit,batch,bash,ssh,devenv,zorynoragent, optionally guarded by anifcondition and repaired by anon-failureflow up tomax-retriestimes. AI-agent repair is opt-in — anagentstep runs only when[agent] flow_steps = true, since it mounts your real credentials writable. Theup,buildandsubmitsteps take typed option keys (e.g.commit,builder,skip-check,parallel) mapped to the command's flags, and each is also an implicit-by-name flag onzoryn flow <NAME>(--commit,--builder X,--no-commit, or--<step>.<key>when two steps share a key) that overrides the step's config value for one run. An unset option means the bare command's config-aware default, so a flow build now honours[builders] parallel. Theupstep also takesno-build, which updates the package without building it so a later step can build (a kernel flow can bump the version, generate a signing key, thenbuild).{pkg},{old_version},{new_version},{tasks}and{task}are substituted into commands -{tasks}is every created task id comma-joined so a multi-repo submit (branch = "sisyphus,p11") can drivezoryn task run/add/approveover all of them, and{task}stays the first id for single-repo configs; anupstep that finds no new version ends the run successfully, a failed step saves its position sozoryn flow --continueretries exactly that step, andzoryn flow list/show/--dry-runinspect what would run. Nothing prompts, so a nightly update fits in one cron line
0.50.0 - 2026-08-22¶
Fixed¶
zoryn devenv(podman): a config-layer apt source (apt_config/apt_builder/apt_sources) whose repositories name a different ALT branch than the effective devenv branch is now dropped with a warning, keeping the base image's own apt sources — a globalapt_configpointing at a sisyphus mirror used to replace the p11 image's sources on-B p11and break the environment. An explicit--apt-confvalue is still applied verbatim, and the new--apt-conf ''form is a one-shot reset that ignores any configured apt sourcezoryn task batchrefused the configtask genbatchhad just written: the base member of a gear specsubst group carries an empty value (specsubst = "subpackage="), which the config validator rejected as "has an empty value". An empty specsubst value is legitimate — the base package substitutes the empty string — and the whole pipeline below (git config, gear) already handled it; the validator now accepts itper-host build limits in
[hosts."<hostname>"]of~/.zorynwere never applied: the lookup addressed the section with literal quotes in the key, so it silently fell back to the global[hosts]values. Host-specificmin_free_ram/max_load_avg/max_io_waitnow win, including for hostnames containing dotszoryn task copynow records the created (or--into) task as the last one worked with, so a followingzoryn task runwithout an ID starts that copy instead of the previous remembered task.--dry-runstill leaves the remembered task unchanged.zoryn up --switch-to-upstream-git: the file-move commit step failed withfatal: pathspec '.gear-rules' did not match any fileswhenever the repository kept its rules in.gear-rules—git mvhad already staged the rename, so re-adding the vanished path madegit addbail out. Step 5 now stages.gear/only- git operations on tags whose name starts with a dash:
git merge,git tag -landgit subtree pullreceived the bare tag name and parsed it as an option, failing with "unknown switch" (or worse, acting on the option). Tags are now passed after an explicit--separator or asrefs/tags/<name>, matching the call sites that already did this, andvalidate_tag_namerejects a leading dash and the^ { }rev-syntax characters that never belong in a tag name zoryn devenv(podman): the generated Containerfile now gives every feature its own group of image layers — the feature's packages in its own apt layer (with its ownapt-get update), then itsrun/run_usersteps — after a small project-independent base layer (bash, git, rpm-build), and only then the project-specific tail (the project's ownpackages, yourbuild/build_usersteps, theprompt). Previously the very first apt layer baked the per-project package union, so any difference between projects — or any change to the feature list — invalidated podman's layer cache from the first instruction on and every feature installer (e.g. claude'scurl … | bash) re-ran. Now a feature's layers depend only on the features before it: projects sharing a feature prefix share those layers byte-for-byte, and enabling one more feature no longer reinstalls the agents already baked in. The extraapt-get updateper feature is cheap next to re-running installers. Cache keys are unchanged, so existing containers and images stay valid; the new layout applies from the next image build. Note the execution-order change it implies: a feature step does not see the project's packages, yourbuildsteps, or the packages of features after it — a custom feature must declare everything it needs in its ownpackages, as the bundled features already do.- watch files:
@ANY_VERSION@(and@SEMANTIC_VERSION@/@STABLE_VERSION@) now expand with the version in a capturing group, as uscan defines them. The href-matching path used the expanded pattern as-is, so a watch file relying on these macros matched the tarball links but extracted no version from them —zoryn check versionon opari2 reported "No versions found upstream" while the upstream page plainly listed 2.0.10. Patterns with an explicit(...)group were unaffected. zoryn check version/zoryn check upstream:-dnow also enables the watch-file debug trace (hrefs found, compiled patterns, matches), which previously onlyzoryn up -dshowed — the failing check above was undiagnosable from its own debug output.zoryn check version: the git-tags check now resolves the upstream URL the same wayzoryn updoes —.gear/upstream/remotes, then the specVcs:tag, thenURL:, each candidate verified withgit ls-remote— instead of accepting only a fixed list of forges (GitHub, GitLab, Codeberg, sr.ht). A kernel-style spec withVCS: https://git.kernel.org/...reported "No watch file or git URL available" whilezoryn upupdated the same package fine; now both commands agree on the upstream source. Thegit ls-remoteprobe is a network call, so it runs only when the check actually falls through to git tags, not while a watch file answers.zoryn check version: without a.gear/version-uppattern, the git-tags check now anchors on the tag carrying the current version and considers only tags in the same naming style, likezoryn updoes — a repo whose history carries foreign-style tags no longer gets them as the "newest version" (c-ares, which inherited curl's tags, was offered1.34.8 -> 7.19.4fromcurl-7_19_4whileupcorrectly said up to date). The style-blind heuristic remains only as a fallback when no tag carries the current version.zoryn check version: the git-tags check now also honours the release-targets/upstream-branch restrictionzoryn upapplies (.gear/release-targets,.gear/upstream-branch,upstream-branchin[version]of.gear/version-up) — candidate tags are limited to those merged into the locally fetched upstream branch. Without it a kernel repo pinned to the 6.18 series was offered 7.x whileupcorrectly stayed on 6.18. Like in up, a configured branch that cannot be resolved fails the check rather than silently widening the candidate set.zoryn up/zoryn check version: a stale local upstream branch no longer hides new versions. The release-targets/upstream-branch filter answers from the locally fetched branch while check's candidates come fresh fromls-remote, so on a kernel repo that had not been fetched since the last release the check saw the new tag upstream, dropped it as "not merged into upstream/linux-6.18.y", and reported "Package is up to date". The filter now compares the local tracking ref with the remote's branch head (onegit ls-remoteof that single ref, best-effort — an unreachable remote changes nothing) and, when the branch is behind, fetches just that branch (git fetch <remote> <branch>— the incremental delta plus the tags pointing into it, seconds on a kernel repo where a full fetch drags in every series branch and the mainline tags) and answers from the fresh state — the check reports the new version right away instead of asking the user to fetch. A warning ("newer versions may be hidden — run 'git fetch'") remains only when the fetch fails or still leaves the branch out of sync. zoryn upfetches the upstream remote itself before the filter runs, so there the probe finds the branch already in sync and changes nothing
Added¶
zoryn submitin kernel-image mode now accepts multiple repositories (-B sisyphus,p11): the flavour tags are created and pushed once, and each branch gets its own new task carrying all flavour subtasks, chained with the usual inter-task dependencies — replacing the manual "copy the sisyphus task into the branch" step. The restriction dated from before the multi-tag machinery could carry more than the first flavour to later repositories; kernel-module submits stay single-repo (their tags are bound to the branch dist)subtask TAB completion (
task approve,task delete,task copy --subtask,task add ^/) now asks the task API withbrief=2, which answers from its in-memory index without touching disk — completing a subtask no longer stalls on tasks whose full state is expensive to readzoryn task add: the^SUBTASK//SUBTASKpositional suffixes now also accept the fullpkg.git=taglabel printed bytask show, resolved to the subtask number via the task API (a bare package name stays rejected — several subtasks of one task can share the git repo, only the tag is unambiguous). Tab completion after^and/offers both the numbers and the labels, so429379^zo<TAB>expands to429379^zoryn.git=<tag>instead of silently matching nothingzoryn gen version-up: new-u/--updateflag for regenerating an existing.gear/version-upwithout losing manual settings — the generated[version]keys (pattern,template,strip-suffix,create-alias, andfilterwhen--filteris given) are replaced in place, while other sections such as[merge]and manually added keys such asupstream-branchare kept untouched. Previously the only option was--force, which overwrites the whole file; the "already exists" error now suggests bothzoryn up/zoryn check version: the release-targets/upstream-branch filter no longer pays the fullgit tag -l --mergedhistory walk (~12 s and ~1 GB on a kernel repo) on every run. The listing is cached in the git dir (zoryn-merged-tags-cache), keyed by the upstream branch tip and the local tag refs, so it is recomputed only when a fetch actually moves the branch or brings a tag; every other run answers from the cache instantly. When the listing does run and takes over two seconds, a warning still recommends the one-timegit commit-graph write --reachable && git config fetch.writeCommitGraph truethat softens the walk.zoryn devenv: new--no-build-depsflag skipping BuildRequires resolution entirely, so the environment holds only the configured[devenv]packages. Needed for specs whoseExclusiveArch:/ExcludeArch:shuts out the host architecture (e.g.u-boot-*edited on x86_64 and built on an aarch64 builder):rpmbuild -bErefuses such a spec, so the environment could not be prepared at all. When resolution fails on that rpmbuild error, both backends now print a hint recommending the flag.zoryn agent mcp— new subcommand group registering HTTP MCP servers in local AI coding agents (Claude Code~/.claude.json, opencode~/.config/opencode/opencode.json, Kimi Code~/.kimi-code/mcp.json).install NAME...writes servers from the catalog — built into zoryn (currentlygitoskop, the read-only ALT gitery mirror, URL derived from[gitoskop] url) or defined by the user in~/.config/zoryn/mcp/<name>.toml(a local definition overrides a built-in of the same name) — or a one-off server via--url;uninstall NAME...removes them;listshows what each agent has configured plus the available catalog. Only the named entry is touched, so the rest of each agent's config survives intact: a config that cannot be read or parsed is reported and left alone rather than rewritten from scratch, a symlinked config is followed instead of replaced, an existing file keeps its permissions and a new one is created0600(these files carry credentials), and a target that cannot be renamed over — the bind-mounted config inside a devenv — is rewritten in place. Agents are handled independently: one failing config does not stop the others. Registration is per user, so devenv environments that bind-mount the agent configs pick it up automatically.
0.49.0 - 2026-08-14¶
Added¶
zoryn devenv: new bundled featurepi— installs the pi.dev coding agent into the dev user's~/.local/binand bind-mounts the host's~/.pi/agentand shared~/.agentsdirectories read-write, sharing provider logins, settings, sessions, extensions, skills and packages with the environment while persisting changes made inside it.zoryn devenv list: the listing now has a header and two more columns — the features an environment was built with and the profile it came from (-when it has neither). The container name carries only a cache key, which says nothing about what went into it, so two environments of the same project were indistinguishable in the list; With--verbosethe listing also names, for every project with more than one environment, the labels whose value is not the same in all of them — usually the computed packages or the spec digest, which is what environments with the same features part over.zoryn devenv inspect NAMEstill prints the whole recorded configuration of one.zoryn devenv: new bundled featurehasher— builds packages with hasher inside the environment. hasher-priv refuses to work where it cannot see a real host, so the feature installshasher-kayfabealongside it (0.2.1 or newer; an older one stops the image build with a message rather than half-activating), creates the satellite users for the dev user, and puts wrappers in front ofhshand friends that starthasher-privdon first use — an environment that never builds anything never runs a daemon. It also permits/proc,/dev/ptsand/dev/kvmin/etc/hasher-priv/system: hasher-priv refuses any mountpoint not listed there, and zoryn's own build commands pass--mountpoints=/proc,/dev/pts. The environment getsCAP_SYS_ADMIN(hasher-priv mounts), which weakens isolation, so select it deliberately;/procstays masked as podman left it. Root for starting the daemon comes from hasher-kayfabe's setuid helper, restricted to thehashmangroup, so thesudofeature is not needed — though that group membership is itself a path to in-container root. The hasher workdir is a tmpfs by default ([devenv.features.hasher] tmpfs, set it to""to keep it on the container's overlay), and the host's~/.hasher/configis bind-mounted read-only (hasher_config,""disables) so builds inside use your own hasher settings — the packager above all, which hasher reads from that file and never from~/.rpmmacros; without it every build dies insisyphus_checkwith "wrong PACKAGER". The host's~/.rpmmacroscomes in the same way (rpmmacros) for builds run in the environment directly — gear-rpm and rpmbuild read%packagerfrom there, hasher never does. A feature's read-only mount whose host file does not exist is now skipped with a warning naming it, instead of podman creating a directory at that path on the host and binding that in. Known limit: the container's cgroupfs stays read-only, so builds run without hasher's per-job cgroup limits and print one warning each.zoryn devenv(podman): a[devenv] mountsentry (or a feature's) written astmpfs:<dest>[:<options>]now becomes--tmpfs <dest>[:<options>]instead of a bind mount, and an entry that expands to nothing is dropped rather than passed to podman as an empty-v— which is how a feature whose mount is an option (mounts = ["${TMPFS}"]) is switched off from the config.zoryn devenv: root access inside an environment is now opt-in through the new bundled featuresudo, and images no longer carry a passwordless sudoers rule for every dev user. The rule was dead weight anyway: ALT ships/usr/bin/sudoas mode 4710root:wheel(control sudo wheelonly), so a user outside thewheelgroup could not execute the binary at all and everysudoin a devenv failed withPermission denied. The feature writes the/etc/sudoers.d/<user>drop-in and setscontrol sudo public, leavingsudoersin charge of what may be run. Thesudopackage itself is no longer installed into every image either, so environments rebuild once on upgrade; add-F sudo(or[devenv.features.sudo]) where root is needed.zoryn devenv: new bundled featurechromium— installs Chromium with DejaVu fonts (fontconfig included) so headless browsing and screenshots, typically driven by AI agents, work inside podman devenvs. The basealt:latestimage lacks Chromium's runtime libraries (NSS, the X11 stack, libgbm, at-spi, libasound, …) and any fonts, so both the distro package and agent-downloaded browsers (puppeteer/playwright) failed to start; the feature also appends--disable-setuid-sandboxto/etc/chromium/default, because the setuidchrome-sandboxhelper cannot work under rootless podman while Chromium's namespace sandbox still can — page isolation is kept.zoryn devenv:[devenv] portsentries may now name the host interface or address the port is published on —"eth0:8080","192.0.2.10:18080:8080", or"*:3000"for every interface — so a server in the container can be reached from the LAN and not only from the host. Without a prefix the bind stays127.0.0.1, as before. An interface name is resolved to its first IPv4 address when the container is created, so a re-addressed interface needs no config edit; an interface without an IPv4 address, or an address the host does not carry, is reported before the cached environment is touched. A host port is arbitrated per bind address with overlap semantics — the wildcard conflicts with every address, so the same port may be published on several specific addresses, and changing an entry's bind still recognises the running container's old bind as the one being replaced. Overlapping entries ("8080"with"*:8080", or two entries resolving to one address with different guests) are refused up front, and equivalent spellings of one mapping are deduplicated. When a configured interface has no IPv4 address, entering an already-created environment degrades to a warning that keeps its recorded ports — provided those are still bindable: a recorded address that vanished with the interface is reported clearly (pasta could not bind it at start anyway). Creation always fails without the address.localhostis accepted as a bind synonym for127.0.0.1.[version] patternin.gear/version-up: newxlength specifier matching one or more hex digits ([0-9a-fA-F]+), so git-describe-style tags with a commit-hash suffix are captured — e.g. passt tags2026_07_28.f8df3f1match{major:+}_{minor:+}_{patch:+}.{build:x}. Previously only all-digit components could be captured, so such tags were invisible tozoryn up.zoryn task add: new--dependent-on PKGoption for therebuildaction — computes the rebuild set from the repository dependency graph instead of naming packages. zoryn takes the source packages whose binaries runtime-depend on PKG (RDBwhat_depends_src,dptype=binary) and keeps only those pinned to PKG at its exact EVR (RPMSENSE_EQUALwith a release, checked viabinary_package_dependencies), so only dependents that actually go stale are rebuilt:--dependent-on kernel-image-6.18selects everykernel-modules-*-6.18(the same set girar's server-sidekmodulesverb produces), while soname consumers and unversioned runtime users (digikam linking libMagickCore, packages that merely callconvert) are skipped and listed. A binary subpackage name is resolved to its source package automatically (--dependent-on libImageMagick7.10). For an update that knowingly breaks ABI (soname bump),--abi-breakadds all direct runtime dependents instead — in topological build order, so inside the task each consumer rebuilds against the new library.--dry-runpreviews the set without touching the task; the branch comes from the task itself.zoryn up: automatic version discovery can be pinned to an upstream branch — the newupstream-branchkey in[version]of.gear/version-up, or the ALT kernel-team.gear/upstream-branchfile (the key wins). Only tags reachable from that branch become update candidates (current-version matching is unaffected); an unresolvable branch aborts the update, and a tag younger than one hour is skipped unless--force. Kernel repositories with.gear/release-targetsrefuse updates on ALT branches absent from the file, andRelease:resets to the branch's traditional base (alt2onc10f2/c9f2) inup,up --switch-to-upstream-git, andspec version set.--tagbypasses the restrictions.zoryn task delete/task delsub: the task ID may now be omitted — with a singlePACKAGE|SUBTASKargument the command targets the last task you worked with (recorded bysubmit,task run ID,task new, …). Because the target task was never typed explicitly, zoryn first lists the task's subtasks, shows exactly which subtask is about to be removed and asks for confirmation (y/N, Enter aborts);-y/--yesskips the prompt and is required when stdin is not a terminal (#139).task batch: newautopackage mode and a globalmodekey in the[batch]section. In auto mode each package's spec version-release is compared with every target repository (RDB): an already-built NVR is added as a plain rebuild, a newer spec gets its own<version>-<release>tag and a source build, and repos ahead of the spec are skipped with a warning. Unknownmodevalues in batch configs are now rejected instead of being silently treated asbuild. Resolution queries RDB by the spec's ownName:— specsubst renames are not applied, and zoryn now warns once per package when a specsubst-renamed auto package is resolved.--refreshis not supported for auto-mode packages and now fails the package up front with a clear error instead of silently pushing a main-derived tag.--skip-built(test mode) now also recognisesautopackages, matchingbuild. Auto packages get gear-create-tag's default tag annotation (their own NVR) —tag_message/tag_formatno longer leak a main-derived message onto their tags. An auto package listed in the config without a local checkout (its directory does not exist) is added as a plain rebuild instead of being skipped.specsubst variable names and values are now validated everywhere they are taken from user input (batch configs,
.gear/version-up[batch], tarball creation) before being written to git config asgear.specsubst.<name>. The error names the offending character with its codepoint and Unicode name, so a Cyrillic lookalike letter (e.g.сU+0441 instead of ASCIIc) — which git rejects with a bare "invalid key" and gear then silently ignores — is explained instead of failing cryptically.- devenv features: new
capabilitiesandsecurity_optskeys infeature.toml—--cap-addnames and--security-optentries applied at container-create time, flowing likedevices(in the cache key only when non-empty, refused byfeature add). Both are strictly validated at parse time since they reach the podman command line. The bundledpodmanfeature now requestsSYS_ADMIN(podman's default seccomp profile allowssethostnameonly for containers withCAP_SYS_ADMIN, and the inherited filter cannot be relaxed from inside — the capability keeps the default seccomp filter fully active, unlikeseccomp=unconfined, and is confined to the container's own user namespace in rootless mode) andunmask=/proc/*(mounting a fresh procfs in a user namespace requires a fully visible/proc); with these, a nestedpodman runinside the devenv works end to end. - devenv features: new
deviceskey infeature.toml— host device paths exposed to the podman container via--device, flowing likemounts(fixed at container-create time, so a changed device set recreates the container; a device missing on the host is skipped with a warning). The bundledpodmanfeature now requests/dev/net/tun, which pasta — the inner rootless podman's network backend — opens to create its tap device; without itpodman runinside the devenv failed withFailed to open() /dev/net/tun. - devenv: bundled
podmanfeature — nested rootless podman inside the container (native overlay via host-backed storage, sub-id ranges sized to the outer user namespace); feature rootrunsteps now execute after the dev user is created, with$USERNAME/$USER_UID/$USER_GIDin scope, and after the dev user's home ischown -R'd, so anything such a step writes there stays root-owned
Fixed¶
zoryn devenv: selecting multiple features that bind-mount the same container destination no longer makes podman fail withduplicate mount destination; zoryn keeps the first mount in feature installation order. This allows the bundledopencodeandpifeatures to share the host's~/.agentsdirectory.task batch:specsubst = "none"in the[batch]section now disables specsubst for the whole batch, matching the per-packagespecsubst = "none"semantics — previously the literal stringnonewas passed through, which was harmless for tag creation but produced spurious per-package specsubst output and, with the new auto mode, spurious RDB-name warnings.
Changed¶
zoryn task test-rebuild --package/--packages-file: thename-versionform is no longer accepted — a version cannot be told from the last segment of a name (foo-1.0.0vsperl-MIME-Base64), so a string without an-altNrelease is now read as a package name. Pass the bare name or the fullname-version-release.- Branch build-dependency chains are now read from
alt-releases-matrix(UpgradeChains.p_chain/c_chain) instead of a hardcoded copy inAlt_branches.build_chains, so a new branch added to the matrix (e.g.c11f1) is picked up by rebuilding against the updatedocaml-alt-releases-matrixwith no zoryn code change. zoryn devenv:[devenv] portsnow concatenates the global list from~/.zorynwith the project's own from~/.config/zoryn/projects.d/<project>.toml(likemounts), instead of the project list replacing the global one — a project adds its ports to the machine's. An entry present in both layers is published once; an empty project list no longer switches the machine's ports off (there is no per-project off-switch, so the global list should carry only ports wanted by every environment).
Fixed¶
zoryn task test-rebuild --package/--packages-file: a version starting with a letter (lshw-B.02.20-alt1) is no longer swallowed by the name and skipped withnot found in branch— once the-altNrelease is cut off the end, the segment before it is always the version (#141).zoryn devenv: build-dependency resolution now expands the spec with%_sourcedirpointing at the spec's own directory (.gear/) instead of the project root, so dependency-generating macros that read auxiliary source files from%_sourcedir— notably rpm-build-pyproject's%pyproject_builddeps_*, which readpyproject_deps.json— resolve the full set instead of silently expanding to nothing. Previously a package likepython3-module-cherootentered the shell with only its statically-declared BuildRequires installed andgear-rpm -baimmediately failed on the missing generated ones. For the remaining blind spot — acopy:/copy?:rule in.gear/rulestaking a file from some other directory — resolution now prints a warning naming the files that%_sourcedir-reading macros will not see.zoryn gen pypi2spec: the cloned repository is now left on asisyphusbranch created from the upstream tag instead of a detached HEAD, so a subsequentzoryn submitno longer fails at the branch push step with git'sThe destination you provided is not a full refnameerror.zoryn gen pypi2spec: repository URL lookup in PyPIproject_urlsis now case-insensitive, so packages that declare their labels in lower case ("repository","homepage"— e.g.pyjpegls) no longer fail withNo repository URL found in PyPI metadata. The homepage also falls back to theproject_urlslabel when the deprecatedhome_pagefield isnull, which is the case for anything built with modern setuptools/hatch.- Task manage TUI: multi-line errors from girar (e.g. a failed
task run) now open in a centred scrollable popup instead of being cut off in the status line; text is word-wrapped, scrolled to the actual failure message, and can be copied with the mouse. Status messages with newlines no longer spill over the rows below the status bar (#138). - Interrupting a parallel build (
zoryn build/zoryn up, Ctrl+C) no longer leaks the gear tarball of still-running builds into/tmp: the SIGTERM handler in forked build children now drains the temp-file cleanup registry before_exit(previously it only removed remote apt tmpdirs, and_exitskipsat_exit). A forked child also drops cleanup entries inherited from the parent, so it can never delete the parent's temp files. task mkrepo --cleanno longer depends on girar: the task's branch is now only a hint for builder selection (a failed lookup warns and the removal runs on-b, or on the default builders), so cleanup no longer dies withCannot determine task branch: Task <N> not found— and leaves<repo_workdir>/<task_id>behind — on hosts without a route to girar or for tasks already gone from the queue. Without a branch the host subdirectory is unknown, so both<workdir>/<task_id>and<workdir>/<host>/<task_id>are removed, but only when they really hold a generated repo (repo/rpms) — a workdir is routinely a plain home directory;--forcewaives the check. Generation still requires the branch.
Changed¶
- Internal: TOML parsing and printing migrated from the
toml(ocaml-toml) library tootoml(TOML 1.0.0 compliant, dependency-free); parse errors now carry the otoml message with line/column embedded, and user-visible behavior is otherwise unchanged. task genbatch: the generated batch config is now serialized by otoml instead of a hand-written TOML emitter (escaping and section quoting come from the library; keys are indented under their section headers). The comment header and section order are unchanged.
0.48.0 - 2026-08-06¶
Added¶
[devenv] ports(machine config only): publish container ports to the host loopback —"8080"maps the same port both sides,"18080:8080"maps host to guest, so a dev server inside the sealed container is reachable athttp://localhost:<host port>. Publishing binds127.0.0.1only; a host port may be mapped only once; host ports are probed before the cached container is touched, so a taken port fails with a clear message without costing you a prepared environment or leaving a half-created one; another environment of the same project still holding the port is stopped (not removed — entering it starts it again) so a routine config change does not need a manualdevenv clean --all, and switching back arbitrates the other way — every path that starts a container (zoryn devenv,devenv enter,install,feature add) arbitrates first; the stop happens only after the image build succeeds, immediately before the container is created, so a rejected port list or a failed build never ends a running session; changing the list recreates the container in place (zoryn.portslabel). Ignored whenoutbound_interfaceis unset, since--network hostalready shares the host's ports. (Danil Shein)[devenv.lan]config sub-table (interface+allow, machine config only — never.gear/devenv): filtered LAN access from the devenv podman container. A pasta sidecar attaches a second tap (zoryn-lan0) to the container's namespace and routes exactly the allow-listed prefixes to the LAN interface; everything else keeps the default route and its tunnel-bound socket. The policy is validated up front (link-local overlap, default-route and host-own-address entries rejected, duplicate spellings deduplicated), the routes are reconciled into the running container on everyzoryn devenv/enter/install(editingallowdoes not recreate the container), removing the section tears the sidecar down, andzoryn devenv inspectshows the live routes. A failed attach now tears the sidecar down instead of wedging it, and a stale sidecar pidfile is cleaned up. Settingallowwithoutinterfacenow warns instead of silently disabling LAN access. (Danil Shein)zoryn devenv: new--timezone ZONEflag and[devenv] timezoneconfig key (machine config only) set the podman container's timezone viapodman run --tz— handy for debugging timezone-sensitive code. Changing the effective zone recreates the container from the cached image (tracked via thezoryn.timezonecontainer label); bwrap inherits the hostTZas before. Shell completions suggest IANA zones from the host zoneinfo database.zoryn devenv: new[devenv] dns,dns_searchanddns_optionkeys and matching--dns/--dns-search/--dns-optionflags — resolver settings for both the podman image build and the container. Changing them recreates the container. (Ajrat Makhmutov)zoryn task newnow defaults to thesisyphusrepository whenREPOis omitted, likessh gyle task new(#129).zoryn gitery: when[gitery] loginis unset in~/.zoryn, the login now falls back to the local part of your gituser.emailif it is an@altlinux.orgaddress (e.g.ivan@altlinux.org→ivan).task batch:-bnow accepts a pattern or comma-separated list matching several builders, and builds every package on all of them in parallel (one log per builder in.gear/), instead of warningPattern matched N builders, using first: …and using only the first. This exists to check a batch on different architectures, not to speed it up: packages are still processed strictly one after another and each is built on every builder, so the run is never faster than on a single builder. The batch is deliberately not split between builders — packages inside it depend on earlier ones through the builder's own hasher repo, so every builder needs the complete set.--skip-builtskips a package only when its src.rpm is present on every builder; with several builders the Python auto-deps rebuild retry is skipped (a warning says so at the moment it applies), andrebuild-mode packages run builder by builder (each needs its own RPM sync). While the builds run, each package prints the same live status table aszoryn build— one column per builder, a new row whenever a build stage or result changes. The table itself now lives in one place (Builder.Build_table) and is shared by both commands. A failed fan-out lists each failing builder's exit code and log path.task batch:--skip-builtflag (with--test) — skip packages whose exact src.rpm is already on the builder and whose git repo is unchanged since that buildtask test-rebuild --mkrepo— build a merged task repo (astask mkrepo) for all resolved builders and run every build against it; fixes false negatives when a task renames a package that dependents pull in via a virtual Provides. The generated repo stays on disk; remove it withtask mkrepo <id> --cleaninvoked with the same builder set. Refuses to start when a builder's build command has no{apt_tmpdir}to point at the merged repo, or when its~/.hasher/configsetsunchecked_initroot_cache— hsh then restores the cached apt box and ignores--apt-config, which would build against the wrong repository and still report success. (Aleksandr A. Voyt)zoryn gitery manage: gitoskop's merge tracking against the official gears repos is now shown — a greenin gearsbadge in the repo list marks repositories whose content is fully merged into gears, and branch/tag names already in gears are drawn green in the repo detail panelszoryn gitery manage:vin the commit list toggles between the one-line view and a fullgit log-style view (full oid with decorations,Author:/Date:lines, indented subject); the choice sticks for the session. Ref decorations (HEAD -> branch,tag: …) are colored in both views by the same git-log syntax + theme the commit view uses. The TUI's commit/diff/file highlighting now maps theme colors to the terminal's basic palette exactly like the CLIzoryn gitery log/show/catcolouring, so both render identically (previously the TUI used muted xterm-256 approximations)zoryn gitery manage: the actions menu on the repo detail screen now acts on the selected branch/tag instead of the whole repository — View log, View tree, Set as default branch (branches) and Delete branch / Delete tag (with confirmation; the deletion pushes an empty refspec over SSH, no local clone needed). Repository-level actions (rename, repack, delete repo, clone URL, charset) are offered only from the repo list, soa→ Delete on a branch can no longer remove the entire repository
Changed¶
- Internal: the
zoryn devenvworkflow moved from the CLI layer into a newdevenv_pipelinelibrary with a result-based API (errors are returned, notexited, like the other pipeline libraries); the command's behavior, flags, and output are unchanged. - Internal: the
zoryn submitworkflow moved from the CLI layer into a newsubmit_pipelinelibrary, restructured as an explicit plan/execute pipeline: submission destinations (new task,--replace,--with) are resolved and validated in a preflight pass before any mutation,--dry-runrenders that plan instead of running the steps inline, and execution tracks partial progress (created tags and tasks) so a failure mid-run reports what was already done. Flags and user-visible output are unchanged. - Internal: the
zoryn builder addworkflow moved from the CLI layer into a newbuilder_addlibrary; the command's behavior, flags, and output are unchanged. zoryn buildandzoryn task batchnow share one fork/poll monitor (Builder.Parallel_monitor) instead of carrying a copy of the loop each. The two copies had already drifted apart: forked children now uniformly get their own session, a/dev/nullstdin, and apt tmpdir handlers that clean up before_exit, and an interrupted run waits for its children before exiting rather than returning to the prompt while they are still tearing down chroots. Ctrl-C during a parallel run now raisesSys.Breakinstead of exiting from inside the library, so cleanup runs:zoryn task batchsaves its state and can be resumed with--continue, and the gear tarball is removed instead of being left behind.zoryn gitery manage: the repo detail screen shows two panels (Branches / Tags) instead of three — the Refs panel duplicated the other two entry for entry (gitoskop's/refsis exactly branches ∪ tags) and was removed, giving both remaining panels more room
Fixed¶
- the
spec:path in.gear/rulesis now validated to stay inside the repository — absolute paths,..traversal and symlinked spec files are rejected (with a fallback to a root-level spec), so a malicious repo can no longer pointzoryn up's spec rewrite at files outside the checkout zoryn up/zoryn check: repo-declared upstream URLs (.gear/upstream/remotes, specVcs:/Url:) are now checked against a scheme allowlist (http, https, git, ssh and scp-like syntax) before being handed togit ls-remote— a malicious repo could declare anext::sh -c ...URL and have git execute the command (on git builds that permit the ext transport), or afile:///local path pointing at the local filesystemtask rebuild/task gitclone/task genbatchand other consumers of the RDB task API: package name, version and release coming from the network are now validated at the parse boundary (Rdb.parse_task_packages_json) — a name with path separators or traversal (/home/...,../../x) or a version/release containing/is dropped instead of flowing into download targets, clone destinations and log paths, where it could place files outside the intended directory- internal:
ppx_shell_checknow follows commands built in a prior let-binding (let cmd = sprintf ... in Shell.run cmd) — previously only the direct argument ofShell.run*/Sys.command/Unix.open_process_*was inspected. Commands are classified at the point of binding, so sanitizing a variable after the command was built no longer launders it, and every binder (matchcases,let*, function parameters, type-annotated patterns) shadows outer bindings correctly. The checker is format-aware — numeric arguments need no escaping, and bare%n/%l/%Lno longer shift later%sindices — and trusts internal safe generators; the handful of constructs it cannot prove are annotated[@shell_safe]with justification comments - sandbox: hook sandboxes (
bwrapandhybridrunners) now run with--new-session, detaching them from the controlling terminal — without it, code running inside the sandbox (e.g. a malicious.gear/up.dhook) could push keystrokes into the user's shell input queue viaTIOCSTIand have them executed as if typed after zoryn exits. The interactivezoryn devenvshell (bwrap backend) keeps the user's session on purpose: a new session cannot re-acquire the terminal, and the shell inside would lose job control zoryn submit: squashing of a duplicate release commit now works for release subjects containing the lettert(e.g.1.0.beta-alt1) — the oldStr-based subject check treated\tin a character class as a literalt, so such subjects were never recognized as gear commits- internal: renamed the
versionlibrary (build version string) tobuild_info, removing the name collision withGau_common.Version(version comparison) and theZoryn_versionworkaround alias incmd_check.ml - internal: unified regex usage on the
relibrary —girar,kernel_image,kernel_moduleandtest_supportno longer useStr - internal: folded the two-function
cli_utilslibrary intobin/zoryn/cli_args.mland dropped the unused publicRdb.fetch_urlre-export ofGau_http.fetch_url - internal: deduplicated
read_file_opt(nowGau_common.Io.read_file_opt, was copy-pasted inkernel_imageandkernel_module) and the task-RPM download wrapper (nowTask_repo.download_task_rpms_multi, was copy-pasted intask rebuildandtask test-rebuild) - build: dropped the unused
yamlopam dependency and five unused library entries (rdb_packages,ebuild,pkgbuild,aptconf,host_monitor) from the zoryn executable's dune file zoryn devenv(podman): an IPv4-mapped IPv6 address in[devenv.lan] allow(e.g.::ffff:10.0.0.1/96) is now rejected with a clear message pointing at the plain IPv4 spelling, instead of crashing validation with an uncaught exception.zoryn devenv(podman): a mixed IPv4+IPv6[devenv.lan] allowlist on an interface currently carrying only one address family no longer fails the container entry (the kernel rejects a route whose source address is not on the tap) — the unroutable entries are skipped with a warning naming them, matching the warn-and-enter behavior of the fully-unavailable case.zoryn devenv(podman): installing a kernel as a build dependency no longer generates an initrd or/bootsymlinks — the image now bakes/etc/sysconfig/installkernel(NOFLAVOUR=y,NODEFAULT=y,INITRD_AUTOUPDATE=none) before apt runs, soinstallkernelexits early; besides removing the pointless work, kernel-heavy dependency sets install noticeably faster.zoryn devenv(podman): build dependencies that refuse to install outside a hasher environment (rpm-build-vm-run is not allowed outside hasher environments) now install even when the probing package is pulled in as a DEPENDENCY — e.g. a spec withBuildRequires: rpm-build-vmpullsrpm-build-vm-run, which the old check did not see because it only matched explicitly requested names. The/.host,/.in,/.outmarker directories are now created before every in-container apt install.zoryn submit: creating the package repository on gitery no longer fails for packages removed from sisyphus or last built from an srpm (#133). The gears/srpms source path is now resolved through the RDBclone_urlAPI (likezoryn clone) instead of guessing from the last build task — arebuild-type task used to be misread as gear-based, pointing the clone at a nonexistent/gearsrepo. A failedssh gitery cloneis no longer silently ignored ("Repository may already exist, continuing..."): submit now falls back tossh gitery init-dband pushes the full local history, and only errors out when both fail.zoryn devenv(podman): re-entering a stopped cached environment no longer re-resolves the package'sBuildRequiresevery time. The recorded digest that decides whether the spec changed lives inside the container and is read withpodman exec, which fails while it is stopped — reading as empty, and so as "BuildRequires changed". Every entry after a reboot or a switch between environments therefore ran a full dependency resolution, and failed outright offline, for a spec that had not changed. The container is now started before its digest is read. (Danil Shein)~-expansion of configured paths is now one shared implementation instead of a family of drifted copies (shell, bwrap and podman devenv mounts, sandbox, batch configs, readline completion,[commands]values, and the devenv command itself): no doubled slash when$HOMEends with/(the doubled path could leak into devenv cache keys and needlessly invalidate a cached environment), and~user-style paths are consistently left untouched everywhere instead of being mangled by some copies.task batch: an existing task whose main-package subtask is registered under the gitery repo name (e.g.php.git=8.5.9-alt1for a[php]entry withspecsubst = "none") is found again. Since the specsubst-rename fix the task search matched only the specName:(php8.5), buttask lslists subtasks by repo basename, so such tasks were reported as "No task found". The search now tries both the spec name and the config/directory name, each with the batch tag and the bare version-release.- SRPM lookup in
srpms_path: packages whose version starts with a letter (gnustep-*r36614,lshwB.02.20,retroarch-assetsgit20220113, ~95 sources in p11) are found again. The SRPM filename matcher required the version to start with a digit, both in the local directory scan and in the remotefind -regexsearch, sotask test-rebuildfailed instantly withSRPM not found,task rebuildskipped the package with aNo src.rpm found matching patternwarning, andtask batchinrebuildmode aborted withNo SRPM found. The version may now start with a letter as well as a digit; cross-package prefix matches are still rejected by the anchored-alt<release>structure of the pattern. (Aleksandr A. Voyt) zoryn devenvnow operates on the git repository root when run from a subdirectory (e.g..gear/), instead of treating the subdirectory as the project — running from.gear/used to produce an invalid image tag (zdevenv-.gear-…: invalid reference format) and madedevenv clean/listmiss the project's environments. Outside a git repository the current directory is used as before. The image-tag slug also drops leading non-alphanumeric characters, so a dot-directory project name can never produce an invalid OCI reference.zoryn devenv(podman): builds no longer die withposix_spawnp: Resource temporarily unavailable—[devenv] pids_limitset only the cgroup pid cap, leaving podman's much lowerRLIMIT_NPROCdefault (ulimit -u≈ 512) as the limit parallel builds hit. It is now passed as--ulimit nproctoo, clamped to the host's hard limit. Takes effect on--rebuild.zoryn devenv(podman,outbound_interface): a dual-stack tunnel interface no longer fails container creation with pasta'sExternal interface not usablewhen the default-route template lacks one of the families (e.g. no global IPv6 on the LAN interface) — the egress binding now covers only the families present on both interfaces, keeping the container single-family as documented. (Danil Shein)zoryn up: with a.gear/version-uppattern carrying a name prefix (e.g.Kea-{major:+}.{minor:+}.{patch:+}), the specVersion:is again the bare version. The auto-discovery path took the whole tag as the version and only replaced its separators with dots, soKea-3.2.0becameKea.3.2.0andgear-update-tagthen looked for a tag namedKea-Kea.3.2.0and failed. The literal parts of the pattern are now stripped, as everywhere else.zoryn submitnow derives the gear tag name fromgit config gear.create-tag.name(default@version@-@release@, with@name@/@version@/@release@expanded from the specName:), exactly likegear-create-tag. A stale tag of a different shape sitting on another commit is no longer force-recreated under a name gear would never produce — a differently shaped tag is only reused when it is already onHEAD.--dry-runnow reports what the real run will do (Would reuse existing tag: …when a tag for this NVR is onHEAD, otherwise the name that will be created); previously it always printed<pkg>-<version>-<release>regardless of the configuration and of what the run would actually do (#131).zoryn gitery manage: in the repo detail screen, the Branches/Tags/Refs panels now scroll with the cursor — with more entries than the panel can show, moving the selection below the last visible row used to leave the cursor drawn nowhere. PgUp/PgDn/Home/End also page by the panel height instead of a stale full-screen page size.zoryn devenv: thetimezonekey is now accepted by the[devenv]config schema — setting it no longer printsunknown key 'timezone' in [devenv]on every run.zoryn submit --replace=TASK_ID:Nnow honours the explicit subtask number unconditionally, in every mode: the numbered subtask is replaced even when its git repository name does not match the submitted package (a warning is printed in that case); a:Nnaming a subtask that does not exist in the task is an error. Previously the subtask was silently looked up only among subtasks of the current package, so replacing a subtask of another package was impossible. In kernel/batch-specsubst mode the explicit number overrides value pairing (accepted only when a single tag is submitted; several tags with:Nare rejected). The pairing itself, used when no:Nis given, now matches kernel-image subtasks by the package name in their tag rather than by git repository name, so a subtask built from the official gears repo (/gears/k/kernel-image-<flavour>.git) is found from a locally-named clone (e.g.kernel.git). Girar's transient task lock (task add: task #N is locked, held briefly while girar processes the task) is retried a few times instead of failing at once, and a lock that persists aborts the replace beforetask delsub's removal could be followed by a failedtask add.- A builder whose
builders.dfile has no[commands] buildnow builds with the same command every other part of zoryn reports for it. Builds used a private fallback template that omitted--apt-config(silently ignoring the builder'sreposetting) and, on a remote builder, the tarball path as well;zoryn builder configprinted a third, placeholder string. All three now come from one place. Note for builders withoutrepo: the default command now passes--apt-config=$HOME/hasher_<N>.env/<branch>/apt.conf— the configzoryn builder addwrites. A hand-writtenbuilders.dentry that never went throughbuilder addneeds that file to exist (or an explicit[commands] build); previously such a builder built against hasher's own apt defaults with no--apt-configat all. Note for builders withrepo: the default command now contains{apt_tmpdir}, so the builder's apt config is rendered into a temporary directory before each build (amktempand anrsyncper build on a remote builder) — the same thing that already happened for anyone whose[commands] buildspelled{apt_tmpdir}out. (Aleksandr A. Voyt) zoryn task mkrepono longer aborts with a rawSys_errorwhen the apt config next to the generated repo cannot be written (anaptconfpath occupied by a file, a full or read-only filesystem). The write is best-effort by design — the repository is already built at that point — but the failure escaped as an exception instead, so the run died without ever printing the repo URL. It now warns and finishes. (Aleksandr A. Voyt)- Python auto-deps: the guard that aborts the
pyproject_deps.jsonupdate now checks only the sources named in the build log. It checked every source in the file, so an unrelated one emptied by the resync (pep517when upstream ships nopyproject.toml) rolled back the copied file and left the package unbuildable. zoryn check versionand the no-watch-file git-tags fallback ofzoryn upnow honour the[version]pattern/template from.gear/version-up. Both previously took the generically "latest" remote tag, which could be a non-release tag — llama.cpp reported10103 -> 9794052.- Failures on those two paths are no longer reported as "Already up to date!". An unreachable remote (the message now carries git's own
ls-remoteerror), a pattern matching no tag (the message names the pattern), a remote with no tags or no version-shaped tags, tags yielding no comparable version, and afilternaming a group the pattern never captures are all errors now. Having nothing to compare against yet — a[version]filterupstream has not published a matching tag for, or only pre-releases so far — remains an ordinary result, andzoryn upnow says which of the two it is instead of printing "Already up to date!". zoryn check versionno longer stops at a broken watch file when the spec also carries a git URL: if the watch file cannot be parsed or its upstream listing comes back empty, the remote git tags are consulted instead (with a warning saying why). Previously the presence of a watch file suppressed the git check entirely, so a package whose upstream moved its archive index — ImageMagick, whoseimagemagick.org/archive/is now a redirect page — was reported as up to date while real releases were being missed.zoryn check versionexits 1 when the check itself fails (remote cannot be listed, watch file unparsable or empty, PyPI lookup error, broken[version]config) instead of always exiting 0, so scripts and CI can detect it. It also prints which tag it selected.- A group written by hand in a
[version]pattern (e.g.(mysql|redis)-{major:+}.{minor:+}.{patch:+}) no longer captures a value, so it can no longer shift the placeholders onto the wrong groups:mysql-1.2.3used to bindmajortomysql, yielding a version rpmvercmp could not compare and a permanent false "up to date". A named group ((?<x>…)), which does capture, is rejected for the same reason, as are lookarounds and inline flags — the regex engine does not implement them, and they now say so instead of failing as a generic invalid pattern.(?:…)is unaffected. A malformed pattern fails with a readable message instead of an internalRe__Parse_buffer.Parse_error.
0.47.0 - 2026-07-23¶
Added¶
zoryn gitery: new command for browsing and administering gitery repositories —ls,find-package,log,commit,diff,show,refs,branches,tags,tree,cat(gitoskop HTTP API,--jsonoutput), plus SSH server verbsquota,clone,charset,default-branch,init-db,repack,mv-db,rm-db(mutating verbs prompt for confirmation unless-y/--yes). Shell completion for repository (own repos forrm-db), branch/tag names and-nnamespaces;-d/--debuglogs each request. Adds[gitery] loginand[gitoskop] urlto~/.zoryn.zoryn gitery log/diff --patch/show: colourised, paged output likegit log --decorate/git diffon a TTY; honoursNO_COLOR.zoryn gitery find-package: full match set via paged fetching (client cap 10000) instead of only the server's first 50;-n/--namespace NSto restrict to a root.zoryn gitery manage [REPO]: interactive full-screen TUI for browsing and managing your gitery repositories — repo list with quota/size/date, sort and search; per-repo branches/tags/refs, commit log, show + diff, file tree; administrative actions (charset, default branch, repack, rename, delete, clone URL) plus mark-and-bulk-delete;Tab/pto view another user's namespace read-only.submit --with: TAB now suggests the remembered last task id (#121).zoryn devenv --feature ID/-F ID(repeatable): one-shot additive enable of a devenv feature for a single run, on top of config/profile features, without editing config.zoryn devenv feature add ID...: inject devenv feature(s) into the current running podman container in place (likedevenv installfor packages), without recreating it. Podman-only; mount-bearing features are refused with guidance to use-F.
Fixed¶
gitery manage:Escon the repo list with an active search filter now cancels the search instead of quitting the TUI; the bottom hint reflects it while a filter is active.gitery manage: the actions menu's Delete now bulk-deletes every marked repo when at least one mark is set, matching theDeletekey (and its own help text). It previously always deleted only the repo under the cursor, ignoring the marks.gitery manage: a successful repo delete no longer reports "delete FAILED". Post-delete verification reloaded the repo list through the gitoskop HTTP API, whose server-side index lags behindrm-db, so the just-deleted repo still appeared in the listing. Verification now uses a targetedssh gitery lsof each repo's own directory, which reflects the deletion immediately; confirmed-deleted repos are also dropped from the on-screen listing right away instead of lingering until the gitoskop index catches up, and gitoskop is asked to re-read the deleted repos' data (?reread_data=1) so subsequent reloads see the fresh state at once.task rebuild --parallel: builders sharing an architecture are now deduplicated to one per arch before building (#124).task batch: a subpackage already present in the task is no longer reported as a failure; the "already added" reply from gyle is now recognized anywhere in the line.task batch: find an existing task when the main package's specName:differs from its config/directory name (e.g.php→php8.2).up,spec add changelog: decode HTML entities in Mozilla advisory CVE titles, soCopy & Pasteno longer reaches the spec changelog.
0.46.0 - 2026-07-12¶
Added¶
task refresh: detect subtasks built for the branch from a different sourcecheck upstream: scans the local worktree first when run in the package's gear repo.check upstream: uses watch file named viacopy:in.gear/rules/.gear-rules.check upstream: recognizes barewatchcopy names (e.g.altlinux/watch).clone: configurable clone_url API endpoint via[clone] api_urlin~/.zoryn.--verify-failuresnow re-verifies stalled builds too, not only failed ones: a task RPM can introduce a hang, so a package that stalls with the task but builds cleanly without it is reported as a regression, and one that fails without it as pre-existing. A build that stalls in verification as well stays unresolved and is re-verified on--continue.
Changed¶
clone: remove the[clone] urlconfig; the repo URL now comes from RDBclone_url(#114).
Fixed¶
task batch: TAB now completes the CONFIG argument (named configs + file/dir paths).clone: resolve repos via RDBclone_urlAPI — clones packages lacking task history (#114).gen environment: generated local build command now passes{hasher_dir}, keeping build,check packages, andbuilder shellon the same hasher directory (#120).- docs site: landing-page burger now opens a mobile nav menu instead of doing nothing.
- docs site: TOC renders as a classic tree; sticky title no longer overlapped when scrolled.
- docs site: burger now appears below 1220px, fixing a dead zone where nav vanished (#116).
- docs site: TOC scrolls the current section into view; drawer TOC kept flat (no tree).
- docs site: set site_url — fixes broken sitemap URLs and emits canonical links (SEO).
- docs site: RU/EN landing get proper meta descriptions with search keywords.
check upstream:.gear/watchis now probed even without a.gear/rulesfile (#118).- build: parallel progress table no longer shows stages from a previous build's stale log.
- docs:
gen opam2specno longer lists nonexistent-p/--packagerand--buildoptions. - docs:
check upstreamsynopsis drops the unsupported-B <repo>option. - docs:
task downloadoption corrected from-bto the real-B/--branch. - docs:
task rebuild-bdocumented as builder pattern, not a target-repo flag. submit --help: examples use the real-Bbranch flag instead of the invalid-b.- docs:
gen opam2specdrops bogus--verbose, documents real--get-version/--sync. - docs:
gen opam2spec --tag-prefixdefault is auto-detection, not the literalv. - docs:
gen opam2spec -o/--outputdefaults to.gear/<name>.spec, not stdout. - docs:
check upstreamoutput described accurately (watch file/source/VCS/upstream git). - docs:
[ssh] persistdefault corrected to10m(matches the code). - docs: document
[ssh],[clone],[repoteka],[hosts],[build] packagerconfig keys. - docs:
task copypoints totask managefor deps, not the nonexistenttask deps. - docs:
task test-rebuilddocuments-B,-p,-s,--deps-by-pkg,--log-dir. - docs:
submitdocuments the--no-edit-commitflag. - docs:
gen version-updocuments short forms-t/-ffor--tag/--force. - docs:
task genbatchdocuments short-ofor--output. - docs:
task batchdocuments--no-python-auto-depsand--no-edit-commit. - docs:
builder rundocuments short-efor--execute. - config: drop dead
[batch]/[specsubst]/[version]entries from the~/.zorynschema. - docs:
build/upsynopsis lists--no-python-auto-deps.
0.45.0 - 2026-07-07¶
Added¶
devenv enterwithout CONTAINER (or.) enters the current directory's container.devenv install --containerinstalls straight into an existing podman container.- test-rebuild: builds killed by hasher-priv
wlimit_*limits are stalled, not failed.
Changed¶
gen environment: packager name/email prompts now explain their use and show examples.- build:
alt_releases_matrix>= 0.2.8 is required (known_branchesAPI rename).
Fixed¶
spec version set: release is now reset to alt1 when the version changes (#115).- docs site: page TOC restored in the mobile drawer; sidebars now labeled (navigation / contents).
- docs site: desktop sidebars get their own scroll — long TOCs no longer clipped on short windows.
- docs site: search results are filtered to the current language — no more English hits on RU pages.
devenv: empty apt sources (e.g. blank/etc/apt/sources.list) warn and keep container defaults.devenv: invalid apt sources errors now name the offending file/builder/inline config.devenv enter/remove/inspect: CONTAINER and--usertab completion now works.devenv --mount-bind: relative host paths are resolved against the current directory.submit -B: comma-separated branch completion works again (was shadowed by devenv-B).submit --replace/--with: step-7 upstream deps are now applied to the reused task.submit --replace=TASK_ID: rejects a task outside the-Btarget repository.
0.44.0 - 2026-07-03¶
Added¶
task manage:o/Okeys insert a subtask after/before the current row (#111)zoryn task batch --batch-pkgs PKGSbuilds only the named packages from the batch config (comma-separated), mirroringzoryn build --batch-pkgs. Names must exist in the config or the run aborts listing the available packages; selection keeps config order and combines with--skip, which further excludes from it. Shell completion suggests package names read from the CONFIG argument (comma-aware, dropping already-typed names).zoryn devenv --mount-bind SPECbind-mounts an extra host path into the dev environment for a single run, on top of any[devenv].mountsfrom~/.zoryn. The value uses the same syntax as that config key: a barePATHbinds read-write on the same path both sides,HOST:CONTAINERremaps the container path, and a trailing:romakes the mount read-only (HOST:CONTAINER:ro); a leading~expands to$HOME. The flag is repeatable. With the podman backend the mount is folded into the container cache key, so adding or changing one recreates the container (image layers are reused); with the bwrap backend it is bound at enter time (--bind/--ro-bind), attaching to the existing chroot without rebuilding.- New
inactivity_timeoutfield in[build]section of~/.config/zoryn/builders.d/*.conf: if a running build produces no new log output for this many minutes (default: 60), it is killed as stalled. The clock starts only after the build writes its first byte to the log, so a long silent setup phase is not mistaken for a hang. Set to0to disable. Can also be set once in the global~/.zoryn[build]section as a fallback for builders that do not define it. Displayed byzoryn builder config. The watchdog runs only for parallel builds (2+ builders, no--sequential); zoryn warns when the field is set but the run is sequential. - New
max_log_mbfield in[build]section of~/.config/zoryn/builders.d/*.conf: if a build log grows past this size in MiB (default: 1024 = 1 GiB), the build is killed as stalled — catches builds stuck in a tight output loop. Set to0to disable. Can also be set once in the global~/.zoryn[build]section as a fallback for builders that do not define it. Displayed byzoryn builder config. Likeinactivity_timeout, the watchdog runs only for parallel builds (2+ builders, no--sequential). - Stalled builds now show a distinct
stalledstatus inrebuild-stats.json(instead offailed), making them easy to identify and rerun with--continue. A run that leaves any stalled build exits non-zero, so automation never mistakes an incomplete rebuild for success. - The
[build] timeoutbuilder setting (how long to wait for a free builder slot) can now also be set in the global~/.zoryn[build]section, used as a fallback when a builder does not define it — matchinginactivity_timeoutandmax_log_mb. zoryn submit --no-depsskips auto--depson tasks in fresher repos. Closes #106.- Log highlighting: pytest-style
PASSEDverdicts are shown in green.
Changed¶
- The
[devenv].mountsconfig (and--mount-bind) now also apply to the bwrap backend, not just podman — each spec is bound into the chroot at enter time via--bind/--ro-bind(read-only when the spec carries:ro). Previously extra mounts were silently dropped for bwrap. zoryn task genbatchnow recognises gear specsubst groups (several subtasks built from one git repo, e.g.dune/dune-ordering/dune-dynfromdune.git) and emits them as specsubst variants with a shared relativepathandspecsubst = var=value(value taken from the tag suffix, base gets the empty value). The specsubst variable name is fetched from the repo's.gear/rulesover gitweb; when it cannot be resolved (or no base package is present) the members are listed flat with a note on stderr, and must have theirpath/specsubstset by hand before that group will rebuild. Generated configs are now valid TOML — all string values and dotted table names are quoted (previously emitted unquoted, so the output did not parse).- Mozilla CVE lookup for changelog generation now scrapes the mozilla.org security-advisories website (https://www.mozilla.org/en-US/security/advisories/) instead of the retired
mozilla/foundation-security-advisoriesGitHub repository, which Mozilla is phasing out and which had begun serving mismatched advisory data. The index page maps<product> <version>to an advisory (MFSA) slug, and that advisory page is parsed for its CVE IDs. Advisories that fix several releases in one index entry (comma- or "and"-separated, e.g.Firefox 136.0.4, Firefox ESR 128.8.1) are matched on any listed<product> <version>.
Fixed¶
builder: silencedcd: ...: No such filenoise fromhsh --printenvcalls.devenvbwrap: sources.list-formatapt_configis wrapped in a generated apt.conf for hsh.devenvbwrap:--with-builder-reponow passes an apt.conf file (not a dir) to hsh.- Log highlighting:
error/fail/exceptionno longer lit inside words likeTypeError-None. Closes #110. zoryn devenvno longer runs podman on hosts where it is not installed. Closes #107.devenv --help: no more cmdliner "unescaped '$'" errors on stderr. Closes #108.task manage: removing a dependency now really removes it. Closes #105.task manage: deps screen no longer shows a stale deps list from another task.task manage: adding a dependency uses girartask deps add, so edits can't be lost.task manage: deps screen reports load failures honestly and supportsR/F5refresh.task manage: after girar actions the task is re-read bypassing the API cache.zoryn up/zoryn specwithparser = "oracle-csaf"no longer adds CVEs whose fix is in a version newer than the update target. Oracle CSAF emits one entry per affectedproduct_id, so a single CVE that touches multiple version lines (e.g.8.0.0-8.0.45and8.4.0-8.4.8) becomes several vulnerability records, each with a narrowaffected_versionsrange. The oldcollect_cvesfilter checked every record in isolation: updating8.0.44 → 8.4.8matched the8.0.0-8.0.45record (old in range && new not in range) and reported the CVE as fixed, even though the parallel8.4.0-8.4.8record showed8.4.8is still vulnerable — the actual fix is in8.4.9. As reported, 40 CVEs from the January 2026 Oracle CPU (fixed in MySQL8.4.9) were appended to a8.4.8-alt2changelog entry.collect_cvesnow groups every vulnerability entry by CVE id (after the product filter), concatenates allaffected_versionsranges, and only keeps the CVE whenold_versionis in some range andnew_versionis in none — so a target below the CPU's fix is correctly recognised as still vulnerable and the CVE is dropped.- The Oracle CSAF changelog source now falls back to older quarters when the predicted advisory is not published yet.
zoryn uppredicts the current-quarter CSAF URL from today's date (e.g. in July it asks forcpujul2026csaf.json), but Oracle publishes Critical Patch Updates quarterly and the current quarter's file may not exist for weeks — the fetch failed withHTTP error 404and no CVEs were collected. On a 404 the fetcher now walks back one quarter at a time (jul->apr->jan-> prioroct, up to a year) until it finds a published advisory, emitting a warning for each step. Non-404 errors (timeouts, HTTP 5xx) and URLs that do not follow Oracle'scpu<quarter><year>csaf.jsonnaming are left unchanged. - Shell completion no longer leaks ANSI escape codes into descriptions, which corrupted the zsh completion menu (entries such as
replaceanddebugappeared truncated). The bash/zsh/fish scripts now invokezoryn --__completewithTERM=dumb, so cmdliner emits plain text instead of SGR-styled doc strings. Closes #98. zoryn devenvno longer rebuilds the podman image when only the bind-mounts change. Bind-mounts are runtime-vflags applied at container-create time and never affect image content, but they were folded into the image tag, so adding/changing a mount (--mount-bindor[devenv].mounts) produced a new tag and re-ranpodman build. The image tag now ignoresmounts(only the container name distinguishes mount sets), and the image build is skipped entirely when the tag already exists — so a mount change recreates just the container and reuses the existing image.zoryn task batch --testnow writes the build log again. Local test builds ran with no log file at all, sohshoutput was streamed to the PTY and discarded — only stale logs from earlier non-test runs remained on disk. The log is now written to.gear/<log-filename>inside the package directory, the same placerun_step_builduses. The filename now feeds the specsubst value into the{batch}template variable (aszoryn builddoes), so each variant of a specsubst group gets a distinct log (e.g.build.ordering.<builder>.log) instead of every variant overwritingbuild.<builder>.log. The path is also fixed to be relative to the package directory the step alreadychdir'd into: it previously re-joinedpkg_path, so a relative--basedirdouble-nested the log under<pkg>/<pkg>/.gear/where nobody looked for it.zoryn task rebuildandzoryn task test-rebuildnow kill the entire process group when a build worker is interrupted or torn down, so descendant processes spawned by the local build tool (hsh,gear, etc.) cannot outlive the worker.
0.43.1 - 2026-06-30¶
Changed¶
- The installed zoryn Agent Skill reference (the pointer
zoryn agent skill installwrites into~/.claude/skills/~/.agents/skills) now embeds self-contained "build inside a devenv" guidance directly, instead of relying only on the full skill under/usr/share/zoryn/. Inside a zoryn devenv that system path does not exist (the container has no zoryn package installed), so an agent following the pointer could not reach any build guidance. The pointer now tells the agent to detect a devenv by thedevenv-hostname prefix and build locally withgit add -Athengear-rpm -ba --commit >/tmp/build_log 2>&1(reading the log for the result), and that gear-rpm builds under~/RPM/— the standard rpmbuild tree (BUILD/,RPMS/,SRPMS/, …) — for debugging a failing%build/%checkstep. The same guidance was also extended inskills/zoryn/SKILL.mdandskills/zoryn/references/devenv.md.
Fixed¶
zoryn builder shellnow checks the builder's hasher chroot (<hasher_dir>/chroot) before launching and, when it is missing, refuses with a clear message naming the builder and how to create it (hsh --initroot-only <hasher_dir>, or run a build). Previouslyhsh-shellsilently fell back to theworkdirin~/.hasher/config(often under$TMPDIR) and failed with a bafflingcd: ...: No such file or directory.zoryn devenvnow works for domain (LDAP/AD/SSSD) users whose UID/GID is very large. Previously the image build baked the literal host UID into the dev user, which failed for ids above 60000: ALT'suseraddcould not create the per-user tcb shadow directory (Failed to create tcb directory), and a rootlesspodman buildcould notchownto an id outside the user'ssubuidrange (chown: changing ownership of '/home/<user>': Invalid argument), so the build aborted at the~/.bashrcstep with/home/<user>/.bashrc: No such file or directory. zoryn now bakes the dev user at a fixed in-range id (1000) when the host id is out of range and remaps the real host id onto it at runtime with--userns=keep-id:uid=1000,gid=1000, so bind-mounted project files stay owned by the real host user. In-range ids keep the previous 1:1--userns=keep-idmapping unchanged. The build also now guarantees the home directory exists (mkdir -p+chown -R) regardless ofuseradd's outcome, creates the dev user withuseradd -o/groupadd -o(non-unique) so a fallback id already present in the base image does not break the build, sanitizes the baked user name (a qualified domain login likejdoe@corp.example.comorCORP\jdoebecomesdevuser, sinceuseraddrejects such names and a.in the name makes sudo silently ignore thesudoers.ddrop-in), and no longer discardsgroupadd/useraddstderr, so any future failure is visible in the build log. Closes #97.zoryn buildno longer fails withcd: <workdir>: No such file or directorywhen the local[build] commandpasses no explicit workdir argument. Such a command lets hsh pick its workdir from its own config (~/.hasher/config, which may point under$TMPDIR, e.g./tmp/.private/<user>/hasher), but zoryn only created its ownhasher_dir(~/hasher) — so hsh tried tocdinto a directory that was never created. zoryn now askshsh --printenvfor the effectiveworkdirand creates that directory when the build command has no{hasher_dir}of its own; builds whose command does pass{hasher_dir}are unchanged. Closes #96.
0.43.0 - 2026-06-26¶
Added¶
zoryn up --no-gpg-signmakes zoryn's own automated commits (the source-update commit, merge conclusions, the switch-to-upstream-git file move, and release-commit squashing) withgit commit --no-gpg-sign. Letszoryn uprun in environments without gnupg2, or wherecommit.gpgsignis enabled but no signing key is available, instead of failing withcannot run gpg2: No such file or directory. Off by default; your own commits made withzoryn commitare unaffected.- New devenv feature
GLM-claude-code— a clone of theclaudefeature that installs the same Claude Code CLI but mounts a separate profile: host~/.config/claude-glm→ container~/.config/claudeand host~/.claude-glm.json→ container~/.claude.json. Lets a GLM login/config live alongside the defaultclaudeprofile without overwriting it. .gear/version-uptemplatenow supports default values:{patch:0}emits the captured group or0when it is absent. Lets one config keep a fixed version layout when upstream sometimes drops a component (e.g.v5.8-505→5.8.0.505,v5.8.1-506→5.8.1.506), so versions stay monotonic instead of an omitted segment shiftingbuildinto thepatchslot.zoryn devenvnow sets the terminal window/tab title while the dev shell is built and used —Zoryn: devenv <host>: <spec>— so it is visible on which host zoryn devenv is running and for which spec file (falling back to the project directory name when no spec is found). The title is restored on exit.- New
zoryn devenv profile listcommand — prints the names of every[devenv.profiles.<name>]profile defined in the machine config (~/.zoryn) and the per-project local config, one per line. With--verboseit prints each profile's effective[devenv]configuration instead — the resolved keys and features, merging the machine and per-project local configs. Shell completion forzoryn devenv --profilenow calls this command instead of parsing~/.zorynitself, so<TAB>offers the real profiles (including project-local ones) without duplicating the config-parsing logic in the bash/zsh scripts.
Fixed¶
zoryn builder shell/zoryn buildmountpoint auto-fix no longer tells you to put device nodes (e.g./dev/kvm) into hasher-priv'sallowed_mountpoints. hasher-priv rejects device nodes there withlookup_mount_entry: /dev/kvm: mount point is not supported, so following the hint (or thesudoauto-fix) brokehsh-shell/hsh. Entries are now classified: real mountpoints (/proc,/dev/pts,/dev/shm,/sys) go toallowed_mountpoints, while device nodes (/dev/kvm,/dev/net/tun, …) go toallowed_devices. The fix hint and thesudocommand emit the correct key for each group.zoryn builder shellnow checks the mountpoints againsthsh --printenv(known_mountpoints) — the authoritative set hasher itself resolves, which also works when/etc/hasher-priv/systemis not directly readable — and, since the builder is already configured, only warns with a recommendation instead of refusing to open the shell.zoryn builder addstill attempts thesudoauto-fix, but on failure (including an unreadable/etc/hasher-priv/system) it prints theallowed_mountpoints=/allowed_devices=keys to add by hand and continues adding the builder. Device nodes are not listed inknown_mountpoints, so they appear only in the recommendation. (Closes #95)zoryn devenvargv reordering now recognises theprofilesub-command, sozoryn devenv profile listreaches the right command. The option-hoisting helper scanned for the first known devenv sub-command name and, not knowingprofile, skipped it and hoisted the innerlisttoken to the devenv level — runningdevenv listwith a strayprofileargument instead.zoryn up --switch-to-upstream-gitnow force-stages.gear/withgit add -fat every staging point — the Step 5 file-move commit (spec, rules, and any moved patch files), the rewritten.gear/rulesduring the unrelated-histories merge, and the.gear/version-upscheme override written at the end. In repos whose.gitignorematches.gear(e.g.openimagedenoiseignores all dotfiles via.*), the switch previously failed withgit add (extra stage paths) failed/The following paths are ignored by one of your .gitignore files: .gear.zoryn up --switch-to-upstream-gitnow writes a templated, self-excludingtar:directive in the rewritten.gear/rules:tar: v@version@:. exclude=.gear/**instead of the literal resolved tag (tar: v2.3.3:.). The version part of the tag is replaced with@version@so futurezoryn upsubstitutes the current version, andexclude=.gear/**keeps the merge commit's ALT-specific files out of the generated tarball (a secondexclude=<subdir>/**is added when a preserved subdir likealtlinux/exists).zoryn gen-pypi2spec,zoryn gen-opam2spec, andzoryn gen-watchnow stage their generated.gear/files (.gear/rules, the spec,.gear/watch) withgit add -f, so generation works in repos whose.gitignorematches.gear.- security:
ppx_shell_checkno longer accepts a mixed escaped/raw concatenation as safe. The oldcontains_escapeheuristic flagged an argument as safe when any sub-expression contained anescape/quotecall, soPrintf.sprintf "git checkout %s" (escape tag ^ host)passed the check while leaking the rawhostinto the shell. The newis_shell_safepredicate requires every leaf of a^/String.concatexpression to be provably safe (escaped, a literal, orString.concat sep (List.map escape …)). A standalone-driver regression test (test/duneruntestrule onppx_bad_mixed_concat.ml) now asserts the bypass is rejected at build time. - Devenv TOML parsing (
.gear/devenv,~/.config/zoryn, project-local configs) now prints a warning with the line and column on a parse error instead of silently treating the file as empty. A single malformed entry (e.g. a mismatched quote) used to makezorynignore the wholepackageslist with no diagnostic, so configured packages never reached the dev environment. zoryn up(automatic tag discovery) now derives the new spec version through the version-uptemplate, matching the alias tag it creates. Previously the spec version came from raw git-tag parsing while the alias used the template, so a reshaping template (optional segment or{patch:0}default) produced a spec version that disagreed with the alias tag andgear-update-tagfailed withTag "…" not found. The explicit--tagpath was already correct.- Sandbox (
bwrapandhybridrunners) now pass the build script and its arguments to the innerbash -cas positional parameters ("$@") instead of interpolating them into the single-quoted command string. The old form let the inner shell re-parse already-escaped text, so a repo path containing a single quote or other shell metacharacter could break the quoting.
0.42.0 - 2026-06-24¶
Fixed¶
zoryn check versionandzoryn check upstreamno longer require--package— when omitted, the package name is read from theName:field of the.gearspec in the current directory, matching the documented default. Closes #91.zoryn upon a PyPI-backed package now prints a note when PyPI is up to date but a newer final-release git tag exists locally, pointing atzoryn up --tag <tag>to merge it, instead of silently reporting "Already up to date". Closes #93.zoryn task addnow accepts an omittedTASK_IDand targets the last task worked with, sozoryn task add copy <pkg> <repo>works right aftertask newinstead of failing withinvalid TASK_ID: copy. A successfultask addalso records the task.zoryn task run/task showwithout a task ID now reuse the last task zoryn worked with instead of gyle's highest-numbered task. Commands that target a concrete task (submit,task run TASK_ID,task new) record it in$XDG_STATE_HOME/zoryn/last-task; the omit-the-ID forms read that first and only fall back to gyle's latest when nothing is recorded. Closes #90.zoryn upon kernel-style specs no longer overwritesVersion: %kversionwith a literal version. When the version is composed by concatenating macros without a joining dot (e.g.%kernel_base_version%kernel_sublevelwithkernel_sublevel=.35), it now walks the template positionally and bumps the right leaf macro (%kernel_sublevel→.36), leaving theVersion:macro reference intact. The macro line's original indentation and name/value alignment (tabs included) are preserved.
Added¶
zoryn devenvbundledkimifeature: installs the Kimi Code CLI (MoonshotAI) into~/.local/kimiand bind-mounts~/.kimi-codeso login, config and sessions persist.- Bundled zoryn Agent Skill now tells agents that inside a devenv (hostname
devenv-*) the package is built locally withgit add -A+gear-rpm -ba --commitrather thanzoryn build/hasher, since theBuildRequires:are already installed. devenv --with-task-repo <id>: install packages from a gyle task's built RPMs inside the dev environment (downloaded into a local cache, exposed as an additive apt-rpmrpm-dirsource).devenv --with-builder-repo <builder>: install packages from a builder's hasher output repo inside the dev environment (additive apt-rpmrpm-dirsource; podman and bwrap).zoryn submit -m/--messagesets the build reason passed to gyle when the task runs, overriding the auto-detected reason for stable branches (and adding one on sisyphus).-m -reads the message from stdin, mirroringzoryn task run.devenvpodman: generate the container's aptsources.listfrom a builder, apt-config path, inlineapt_sources, or--apt-conf, replacing the base image's sources; localfile:repositories are bind-mounted read-only.zoryn devenvSSH agent forwarding (podman):--ssh-agent/-Aor[devenv].forward_ssh_agent = truebind-mounts the host$SSH_AUTH_SOCKinto the container (fixed/run/ssh-agent.sock+SSH_AUTH_SOCKenv), so agent keys — including those forwarded by anssh -Alogin — work inside. The socket is out of the cache key; the container is recreated when the socket changes between logins.zoryn devenvbundledsshfeature: installsopenssh-clientsand bind-mounts the host's~/.sshread-only, so keys,configandknown_hostsare available inside (e.g. for git-over-ssh and remote builders).zoryn devenvbundledzorynfeature: installs thezorynpackage from the container branch's repo and bind-mounts the host's~/.zorynand~/.config/zoryn(builders, settings) read-only, sozorynruns inside the dev environment against your host configuration. The package ships its Agent Skill under/usr/share/zoryn/skills/, so no skill mount is needed.zoryn agent skillmanages the bundled zoryn Agent Skill (agentskills.io open format) for local AI agents.zoryn agent skill installwrites a small referenceSKILL.md— the skill's frontmatter plus a pointer to the full skill shipped with the package at%_datadir/zoryn/skills/zoryn/SKILL.md— into~/.claude/skills/zoryn/(Claude Code, opencode) and~/.agents/skills/zoryn/(Codex, opencode). One source of truth that updates with the package; sound because the skill only matters where zoryn is installed (so the referenced file is always present, including inside a devenv). Alsoagent skill uninstall,agent skill status(where installed / whether the reference resolves),agent skill pathandagent skill show.--diroverrides the targets,--forcereplaces a previous zoryn reference. Install and uninstall only ever touch folders zoryn itself wrote (identified by a marker) — a hand-written skill or unrelated files are refused/left untouched, never deleted.zoryn devenv --debugnow dumps every cache-key input (packages, build, mounts, features, prompt, image, branch, spec_hash, …) plus the resulting key and whether it was a cache hit/miss — so two runs can be diffed to see why an environment rebuilt.zoryn devenv --rebuilddrops the current environment's container and image and recreates them (podman), honouring all other options (profile, branch, …); reuses the podman build layer cache unless--no-cacheis also given.--no-cacheis now a pure build modifier (passespodman build --no-cache) that on its own neither forces a rebuild nor deletes anything — combine--rebuild --no-cachefor a full from-scratch rebuild.zoryn devenv(podman) no longer rebuilds when the spec changes: the spec is out of the podman cache key, so the existing container is reused and itsBuildRequiresare reconciled — re-resolved inside the container and the missing onesapt-installed (idempotent). A digest of the spec'sBuildRequireslines, recorded inside the container (/var/lib/zoryn-devenv/br_digest), lets an unchanged spec skip re-resolution entirely. The resolved deps are recorded per hasher pass (build_requires_pre,build_requires) and shown byzoryn devenv inspect. RemovedBuildRequiresare left installed (usecleanfor a fresh env). bwrap still keys on the spec (chroot rebuilt on change).zoryn devenvnow records what each podman container was built from aszoryn.*labels at creation (cache key, image, branch, packages, features, mounts, build steps, spec digest, …);zoryn devenv inspect <CONTAINER>prints them, so environments self-describe and two can be diffed to see which input differs.zoryn devenv remove <CONTAINER>removes one podman dev-environment container directly by name/ID (fromzoryn devenv list) together with its backing image (non-force). Refuses non-zoryn containers.zoryn devenv enter <CONTAINER>enters an existing podman dev-environment container directly by name/ID (fromzoryn devenv list), starting it if stopped, with no project-config resolution —--root/--userto pick the user,-- CMDto run a command. Refuses non-zoryn containers.zoryn devenv clean --project PATHremoves every podman dev environment of the project atPATH(its directory path, as shown byzoryn devenv list, or its basename) from anywhere — without being in that directory and without needing a resolvable config there.zoryn devenvbundledcodexfeature (installs the OpenAI Codex CLI via its official installer; bind-mounts~/.codexso its login persists).zoryn devenvbundled CLI features (claude,codex,opencode) now add their install dir toPATHin the dev user's~/.bashrc(claude/codex →~/.local/bin, opencode →~/.opencode/bin), so the installed tool is found in the shell.zoryn devenvfeature options can now parameterisemountsvia${NAME}substitution; the bundledvimfeature gains aconfig_access(ro/rw) option controlling whether~/.vimis mounted read-only (default) or read-write.zoryn devenvbundledvimfeature (installsvim-console+vim-plugin-spec_alt-ftpluginand bind-mounts the host's~/.vimrcread-only and~/.vimread-write); mount~/~/now expands on the container side too (not just the host), and read-only (:ro) mount sources are no longer auto-created when missing.zoryn devenvfeature selection is per-layer like every other[devenv]key: when a profile is active only its[devenv.profiles.<p>.features.*]tables apply; the base[devenv.features.*]layer is not inherited into profiles.zoryn devenvmounts now expand a leading~/~/(host side) to$HOME; the bundledclaudefeature mounts~/.claudeand~/.claude.jsonso its login persists.zoryn devenvfeatures: select reusablefeature.tomlbundles via[devenv.features.<id>]tables (devcontainer-style; the table's keys are the feature's typed options). A feature contributes packages +run/run_usercommands + mounts (features-first), with options passed as an env prefix. Bundled features are embedded in the binary; a local~/.config/zoryn/devenv/features/<id>/overrides them. Bundled:claude,opencode. Podman backend only; machine config only.zoryn devenvconfig profiles:[devenv.profiles.<name>]is a fully independent[devenv]configuration (changed from the earlier overlay behaviour) — when a profile is active every key is read only from it, the base[devenv]is not inherited or extended; the base applies only when no profile is active. Selected with--profile/-por[devenv].default_profile. A package'sBuildRequiresand committed.gear/devenvpackages still apply regardless of profile. New[devenv].branchkey (defaultsisyphus) sets the target ALT branch and derives the default podman base image, so e.g. sisyphus and p11 environments coexist and switch by flag.--branch/-Bis a one-shot branch override. Machine config only (~/.zoryn,projects.d/).zoryn devenvbwrap backend:[devenv].apt_configpasses a host apt config path tohsh --apt-config, so the chroot is populated from chosen repositories (e.g. a non-default branch) instead of the host hasher default. Folded into the bwrap cache key; the podman backend also honours it as the container'ssources.list(when noapt_builder/apt_sourcesoverride is set), folded into the podman cache key viaapt_sources_list.zoryn devenvnow resolves a package'sBuildRequires:inside the prepared environment in two passes (modelled on hasher'shsh-rebuild): pass 1 installsBuildRequires(pre):(macro-defining packages), pass 2 runsrpmbuild -bEand installs the full expanded set — so rpm macros and%ifconditionals are honoured for the target arch, replacing the previous host-side text parse. Also picks up dynamicBuildRequiresinjected by build-body macros (%pyproject_build,%set_gcc_version, …) via a%{?_buildrequires_build}dump, and resolves capabilities with several providers by passing apt'sAPT::Install::Virtualoptions. Both backends; the spec digest is part of the cache key. (rpm-buildis now always present in the environment.)zoryn devenvpodman backend: configurable shell prompt via[devenv].prompt— a literal bashPS1baked into the image's user~/.bashrc(machine config only; changing it rebuilds the image).zoryn devenvpodman backend:[devenv].outbound_interfaceforces the container's outbound traffic out a host interface via a rootlesspastanetns (--outbound-if4/--outbound-if6/SO_BINDTODEVICE, with the host default-route interface as the pasta template, so point-to-point/tun/WireGuard interfaces work) instead of the default--network host(machine config only; changing it recreates the container).zoryn devenvpodman containers now run with--init(a minimal PID 1 that reaps zombies — without itgit's background auto-gc and other build/test subprocesses pile up as defunct processes and exhaust the pid limit) and a configurable--pids-limitvia[devenv].pids_limit(default 4096,0= unlimited).zoryn devenv— open an ephemeral development shell with a package's build dependencies plus configured extras, with the project directory bind-mounted (bwrap and podman backends).zoryn devenv install PKG...— install extra packages into the current package's prepared environment (preparing it first if missing);--savealso appends them to.gear/devenv.zoryn devenv clean— remove the current package's cached dev environment (bwrap: hsh-rmchroot + cache dir; podman: removes the persistent container and the built image).--all(podman) removes every environment of the current project regardless of cache key, sweeping stale containers left by config changes, without touching other projects (matched by azoryn.projectlabel).zoryn devenv --image IMAGE— override the podman base image (config[devenv].imageor this flag); default isregistry.altlinux.org/<branch>/alt:latest.zoryn devenvpodman backend builds a small derived image (FROMthe base, with apt deps + a user matching your host UID/GID + passwordless sudo provisioned at build time), then runs a persistent--userns=keep-idcontainer (one per dependency set) entered viapodman execas you (non-root) — project files stay owned by your host user, and liveinstallrunsapt-getas in-container root (podman exec --user root) with no image rebuild. Provisioning at build time (as root, before the keep-id remap) fixes theaptlock/permission errors of the previous keep-id-then-provision approach. With no backend specified it auto-enters whichever backend already has a cached environment.zoryn devenv --user/-u USER— podman only: enter the container as a specific user (mapped topodman exec --user).zoryn devenvaccepts shared options before the sub-command, e.g.zoryn devenv --backend podman install vim.[devenv] build— array of shell commands run as image-buildRUNsteps (podman backend; as root, after dep install, before the user switch). Machine config only (~/.zoryn,projects.d/), not.gear/devenv.[devenv] mounts— array of host paths /host:container[:opts]specs bind-mounted into the podman container via-vat create time. Machine config only (~/.zoryn,projects.d/), not.gear/devenv.zoryn devenvnow defaults to the podman backend when podman is installed (falling back to bwrap), when no backend is configured or cached.
0.41.0 - 2026-06-15¶
Added¶
- Config files now warn on unknown sections and misspelled keys (
~/.zoryn,.gear/version-up,projects.d/*.toml,builders.d/*.conf, and batch configs~/.config/zoryn/batch/*.conf). - submit:
--withsearch forms (PKG,^PKG,PKG:) now also acceptpkg.git=tag, finding the task that contains that exact subtask instead of matching by package name only. - submit: autodetect kernel-image repos (
.gear/ruleswithspecsubst: kflavour, specName:containing@kflavour@) and create the tag with-s kflavour=<flavour>, fixinggear-create-tag: specsubst variable "kflavour" is not defined. The flavour comes from-k,[specsubst] kflavourin.gear/version-up, or the branch's<flavour>/<dist>prefix (which also defaults-Bto<dist>). - gen environment:
-dnow traces the whole decision path — tool availability checks, SSH key lookups,~/.gnupgpermission check, keystore classification (entries found inprivate-keys-v1.d), every gpg/git command with its exit status, and the chosen key-generation path.
Fixed¶
- sandbox: disable commit/tag signing for hooks run in the sandbox. The empty-home tmpfs has no GnuPG keyring, so a signed
git commitin a.gear/up.d/hook failed with "gpg: skipped … No secret key"; zoryn now forcescommit.gpgsign=false/tag.gpgsign=falsevia theGIT_CONFIG_*env override inside the sandbox. No keys are forwarded in — sign on the host instead. - build:
Builder.build_filenow recreates a missinghasher_dir(e.g. a symlink to tmpfs gone after reboot) before building, fixingtask rebuild,build --sectionfallback and batch-pipeline paths that called it without the prior status check. - batch: RPM sync to/from the hasher (
Builder.run_sync,Builder.sync_rpms_to_hasher) also recreates a missinghasher_dirfirst — in batch rebuild mode the upload sync runs before any build and silently failed on a dangling symlink, building without the accumulated RPMs. - sandbox: hasher and hybrid runners recreate a missing
hasher_workdir(e.g. a symlink to tmpfs gone after reboot) before initialising the chroot, instead of failing the hook. The symlink-resolving directory check is now the sharedGau_common.Shell.ensure_local_dirhelper, also used by the builder. - security:
Rpm_spec.update_versionnow validates the new version against an allowlist (alphanumeric and. - _ + ~, bounded length) before writing it, rejecting an upstream-derived value that contains spaces, control characters or RPM macro triggers — the spec is left untouched on rejection. This wires up the previously-unusedValidation.validate_versionguard on theup/gen/specversion-bump paths. - security: enable the runtime shell-injection safety net in the production binary. Every command, after template expansion and just before it reaches the shell, is scanned for live
$(...)/backtick/${...}substitution outside single quotes; a match prints a warning (non-strict — never aborts). Previously this defense-in-depth layer was compiled in but dormant (only the test suite turned it on). The quote-state tracker is backslash-aware, so the nested-SSH'\''idiom (e.g. remote$(...)inzoryn builder status) is not flagged. - build: build tarballs are now removed on every path — normal return, exception, explicit
exit, and Ctrl-C — via a newGau_common.Cleanupprocess-exit registry (zoryn build,zoryn build --section, TUI mode,task rebuild, auto-fix retry). Previously the trailingSys.removeran only on success, leaking the$TMPDIRtarball on any other path. The top-level SIGINT/SIGTERM handlers now exit cleanly soat_exitruns, and the apt-config tmpdir cleanup is registered through the same coordinator. - build: forked build children (parallel
zoryn build, TUI mode) no longer leak the per-builder apt-config temp directory on the remote builder.Unix._exitskipsat_exit, so these children now explicitly wipe the inherited cache and clean up their own apt tmpdirs on every exit path — normal, exception, and SIGINT/SIGTERM — via the sharedBuilder_apt_upload.init_forked_child/cleanup_forked_childhelpers (the same mechanismworker_poolalready used). - build:
create_gear_tarballno longer leaks its temporary tarball whengear --commitfails — the empty/partial temp file is removed before returning the error instead of being left in$TMPDIR. - gen pypi2spec / gen opam2spec: sanitize upstream metadata (summary, license, version, URL, VCS, description) before writing it into the generated spec, closing an RPM-macro injection where a hostile PyPI/opam
%(…)/%{…}value could execute shell or expand macros when rpm parsed the spec. Single-line header fields also collapse newlines to prevent injecting extra spec directives. - gen environment: empty
private-keys-v1.d/public-keys.ddirectories left behind by an interrupted gpg2 migration no longer flip a classic (secring.gpg) keyring to "modern" — only a real*.keyfile inprivate-keys-v1.dcounts, so key checks go through plaingpginstead of the hanging gpg2/keyboxd (#86). The stuck-lock hint now also suggests removing stale GnuPG lock files.
0.40.0 - 2026-06-09¶
Added¶
- sandbox:
[sandbox] specbrin.gear/version-up. Withspecbr = falsethe hybrid runner skips the package's BuildRequires — the chroot is initialised bare (hsh --initroot-only) instead of building the src.rpm; the configured packages (git,[sandbox.chroot] packages,[sandbox] packages) are still installed. Defaults totrue; supersedes a custom[sandbox.chroot] prepare(warns when ignored). - submit: autodetect kernel-module template repos and create per-flavour specsubst tags (
-kexposed only in such repos, comma-separated and TAB-completing flavours; default flavours come from the module's existing builds).karchis derived from the arches the kernel flavour is actually built for, so a module is not built where its kernel is absent.--dry-runprints each tag's specsubst headers. - submit: universal multi-tag
--replace— for kernel-module flavours and batch specsubst submits, each new tag replaces the subtask carrying the same value (adds it if missing), instead of only the first tag. - task gitclone: new subcommand that clones the git repository of every subtask of a task into the current directory, one per package, at the exact commit built in the task. Fetches from the task's own gears over
git://(resolving archived DONE tasks automatically); skipsdeleteandsrpmsubtasks. - task mkrepo: new (experimental) command — generates a local merged apt repository overlaying a task's RPMs on the base branch mirror, for testing the task against a complete snapshot. The packages to add and remove come from the task's authoritative
plan/bin.list.diff(so additions, version replacements, and deletions — including delete-only tasks — are reflected). Thebase/index is built by splicing pkglists (reuse the mirror'spkglist.classic, drop the removed headers, splice in the task'spkglist.task) instead of runninggenbasedir, so indexing reads only the task's packages and is near-instant regardless of branch size. Writes a single pkglist index — plainpkglist.classicby default, orpkglist.classic.xzwith--compressed(not both).--forceremoves a previously generated repo before rebuilding;--cleanremoves it and exits. A ready apt config (apt.conf/sources.list/priorities) is written to<workdir>/<task_id>/aptconf/, and the generatedreleaseidentifies the snapshot (Suite/Label= branch,Description=<branch> (<branch source task> plus task <id>)). - up:
--reset-to-gearflag — hard-resets the package to its published gears/srpms state before updating, discarding local divergence, uncommitted changes, and untracked files (ignored artifacts kept). Resets the branch planned for the update (current if a known ALT branch, otherwise sisyphus) regardless of the active branch. Closes #81.
Fixed¶
up / spec parsing: the RPM built-in macro
%nilnow expands to the empty string during version computation. A spec composing its version as%base%sublevel%extrawith%define extra_version %nil(e.g. kernel-image) previously yielded a bogus current version like6.18.35%nil, sozoryn upfailed withCannot find any tags for current version '6.18.35%nil'.build (
--section): a short-circuit section build of aspecsubst:template package (kernel-image, kernel-modules) no longer feeds rpmbuild a raw template —rpmbuildfailed withInvalid symbol '@' (0x40) in: Name: kernel-image-@kflavour@. zoryn now expands@var@placeholders before syncing the spec into the chroot, mirroring gear (values fromgit config gear.specsubst.<var>), and errors with a clear hint when a value is unset.- up: the upstream remote is now set up from any forge, not just GitHub/GitLab/codeberg/sourcehut. The cascade (
.gear/upstream/remotes→ specVcs:→ specUrl:) is an explicit upstream declaration, so the first candidate thatgit ls-remotecan reach (probed with atimeoutandGIT_TERMINAL_PROMPT=0) is used — no host allowlist. Self-hosted Gitea/Forgejo/cgit instances (e.g. 0ad'sgitea.wildfiregames.com) work now, and an upstream hosted onaltlinux.space(a Forgejo) is accepted like any other reachable forge — unblocking packages like kitsune-adw. - sandbox: hybrid mode now also bind-mounts the chroot's
/var(read-only) into bwrap, alongside/usr,/lib,/lib64,/etc,/bin,/sbin, with a writable/var/tmptmpfs on top so hooks can use it as scratch. - sandbox: the hybrid chroot is now prepared once per run instead of for every hook. Re-initialising per hook was pure overhead — most visible with
[sandbox] specbr = false, where the bare init (hsh --initroot-only) rebuilt the chroot from scratch before each script. The bare init also streams its output live (its lines aren't recognised as build stages, so the long basesystem step previously looked like a hang). - task mkrepo: a package rebuilt with an identical
epoch:version-releaseis now taken from the task, not the stale base mirror.bin.list.diffomits such rebuilds, so the task's own package set is now authoritative: every task build replaces the same-named base copy in both the symlink farm and the spliced index. - gen environment: GPG key generation no longer hangs silently. zoryn now reads the passphrase itself and feeds it to gpg via
--batch --passphrase-fd(so gpg never blocks on its own/dev/ttyprompt after the migration messages of a legacy~/.gnupg), pre-checks~/.gnupg/$GNUPGHOMEownership and permissions and aborts with a fix command when unsafe, and prints recovery hints on failure. It also detects a classic keyring (secret keys insecring.gpg, no keybox) from disk and drives it withgpginstead ofgpg2—gpg2would migrate it and can hang on keyboxd's lock. Both listing and generation then go throughgpg: a genuine GnuPG 1.4gpggenerates via a batch control file (so the new key lands in the samesecring.gpgthe user reads), while a 2.xgpg(the commongpg→gpg2 symlink) uses the loopback--quick-generate-keypath so it never prompts and re-hangs. The key-listing calls are guarded withtimeout -k, so a wedged agent turns into an actionable "rungpgconf --kill all" error instead of an infinite wait. Closes #86. - commit / changelog parsing: a changelog version containing letters (e.g. AFLplusplus
5.00c) is no longer dropped.zoryn commitproduced a malformed-subject because the header parser only accepted all-numeric versions; it now accepts RPM-legal version characters. Closes #85. - up (scheme detection): a merge of an upstream tag (monorepo
Merge tag 'hatchling-v1.29.0') is no longer misdetected astarball-watch. The detector now defers to the tag-based stages — recognizing prefixedv-version tags like<prefix>-v<version>— and names the actual merged tag in its reason. - up (scheme detection): a merge of an upstream release tag with a non-semver name (e.g. 0ad's
Merge tag 'a27.1') is no longer misdetected astarball-watch. When the merged commit is absent from every fetched upstream branch, the detector now also matches it by SHA against the upstream remote's own tags (git ls-remote --tags), so any upstream tag-naming convention counts as a realgit-merge. - builder shell: now mounts the same paths the build uses (
/proc,/dev/pts,/dev/kvm, …) and validates/auto-fixes hasher-privallowed_mountpointsfirst, so packages whose BuildRequires need/procwork in the interactive shell. - submit:
--replacenow implies--allow-overwrite-tagfor the local tag rewrite. Replacing an in-progress subtask aftergit commit --amendorgit rebaseno longer aborts with "tag points to commit not an ancestor of HEAD" — the whole point of--replaceis to swap in a new commit at the same tag name. A dim notice is printed when the implication is auto-applied.--refreshand the gears-immutability check are unaffected. - submit: brand-new packages (not yet in Sisyphus) no longer get stuck on the "tag points to a commit not an ancestor of HEAD" safety check when a stale local tag remains from a previous attempt. When the gears repo is provably missing (ls-remote fails) and the package is unknown to RDB, the local tag is treated as stale and overwritten automatically with a warning. Existing packages keep the strict ancestor check.
- task test-rebuild: now aborts with an error instead of silently reporting success when the task has no built repository to overlay. Previously a task whose RPMs failed to download, or whose repo was not ready, produced a green run that rebuilt dependents against the clean branch — testing nothing. The download now reports a per-architecture result; an empty listing or any failed RPM aborts the run, as does a run with no available builder architectures. The gate is the task's repository presence, not its state: an early guard only rejects tasks still building (
NEW,AWAITING,PENDING,BUILDING,COMMITTING,FAILING), whoseRPMS.taskwould be empty or partial. Terminal states proceed, includingFAILED— a failed task often still has a usable (if partial) repository, and test-rebuilding it is a normal workflow;DONEwarns the overlay is redundant andFAILEDwarns the RPM set may be partial.--local-rpmsand--without-taskare unaffected. - submit:
--replacewith multi-branch-B sisyphus,p11now finds and replaces the matching task in each branch (one auto-search per branch). Previously the comma-joined repo string was passed as a single filter totask ls, so nothing matched and the command aborted with "no open tasks found". Explicit--replace=TASK_IDis still single-target and errors out when combined with multiple-Brepos.
0.39.0 - 2026-05-26¶
Changed¶
- task rebuild: default
--dptypeis nowbinary(wassource). Binary dependencies match the practical "what breaks if this ABI changes" question better for the rebuild workflow. - up (merge-hooks):
.gear/merge-up.d/*hooks now run only when this invocation actually produced a merge commit, or when the user explicitly asked for the stage via--stage merge-hooks/--from merge-hooks. Running the full pipeline on an already-up-to-date tree no longer re-runs hooks that already ran in the previouszoryn up(and that had no merge in this run to react to). The persistent pipeline state grew amerge_performedflag (set bystage_mergeon success, preserved across--continue); explicit--stage merge-hooksinvocations also opt in via a runtime-onlyexplicit_merge_hooksflag that is not persisted, so a re-run never silently inherits yesterday's opt-in. - up: new Step 0.3 syncs the local package repo with the public gears/srpms mirror before detection — fast-forwards when behind, auto-merges on divergence, aborts on conflicts or a refused fast-forward. Only runs when the local branch is a known ALT branch (sisyphus, p11, p10, c10f2, …); topic branches like
masterorwip-*skip silently. Also skipped for packages unknown to RDB, dirty trees, detached HEAD, or fetch failure; fetch is bounded byGIT_HTTP_LOW_SPEED_LIMIT=1000/_TIME=10so a slow mirror can't hang the run. Closes #80.
Added¶
- task copy:
--into <task_id>option to append subtasks to an existing target task instead of creating a new one. Target repo is derived from that task (-Bignored); target-task state is validated (DONE/SWEPT/EPERM rejected, in-progress states warned); dependencies are left untouched. - task copy:
--subtask <SUBTASK>(repeatable, comma-separated) to copy only selected subtasks. Accepts the same syntax aszoryn task delete(numeric ID, package name,pkg.git=tag) with TAB completion from the source task. - task test-rebuild:
--without-taskflag — skips downloading task RPMs and rebuilds dependent packages against the current branch state (baseline). Hasher RPMs are cleared before each individual build to prevent cross-contamination between dependent packages in the same run. Logs get thebaseline.prefix. Mutually exclusive with--verify-failuresand--local-rpms. - task add:
--replaceflag to replace an existing subtask in a single command. Target subtask is derived from the action's package name (matches bothpkg_nameandgit_repo); ambiguous matches error out asking the user to delete the unwanted subtask manually. Cannot be combined with multi-packagedel/rebuild. Closes #76. - task add:
<task_id>^<subtask_id>positional shorthand for--before <subtask_id>— accepts task references in the form printed bytask ls/task show. - task add:
<task_id>/<subtask_id>positional shorthand for--replacetargeting an exact subtask (bypasses auto-find by package name).
Fixed¶
- submit / task batch: tag-overwrite safety rework (Closes #71)
submitrecreates a stale local tag automatically when the existing tag is an ancestor of HEAD (fast-forward case) and not yet published in gears. Refuses with an actionable message otherwise. The sisyphus-onlyAlt_branches.is_rootforce-tag shortcut is gone — uniform algorithm on every branch.--allow-overwrite-tagflag (on bothsubmitandtask batch) bypasses the gears/ancestor checks when you know the stale tag is unrelated to HEAD.--refreshcontinues to overwrite the local tag unconditionally regardless of this flag — it is only relevant on non-refresh submits.- Output distinguishes
Created tag: X(fresh),Reusing existing tag: X (already on HEAD), andRecreated tag: X (was at <sha>)(force-overwrite). Previously every successful tag step printedCreated tageven when no tag was created. --dry-runnow refuses (exit 1) when the gear specfile is missing or lacksVersion/Release(previously synthesized?-?placeholder tags).- Same safety algorithm applies to all batch tag paths (simple, first, remaining) and is consistent with non-batch submit.
- Side-effect: in batch mode with
includes_original_first=true, the first tag is now built viaapply_batch_formatlike the rest of the batch. If you relied on the implicit<project>-prefix, add{name}totag-namein[batch]. - Simple-submit defers to
gear-create-tagdefault naming when no local tag exists. Previously the fix forced<project>-<version>-<release>style, breaking packages that historically used bare<version>-<release>tags (e.g. libva-intel-media-driver). Safety algorithm still runs when an existing tag is found under either naming style. - task copy: dry-run/real run on a NEW source task whose subtasks have not been fetched yet (no
srpm/package/pkgnamein the API payload) no longer aborts with "No valid subtasks found".resolve_pkg_namenow falls back to the git repo basename fromdirso build subtasks are recognised before any artifact has been produced. - builder:
clear_hasher_rpmsnow also wipes{hasher_dir}/repo/SRPMS.hasher/.sync_rpms_to_hasherdoes the same only whendelete = true(its default, used byzoryn task test-rebuild);zoryn task rebuildcalls it withdelete:falseand keeps the additive-sync semantics. Output ofhshpreviously accumulated indefinitely across a test-rebuild batch. - test-rebuild:
fork_single_taskchild now clears the inherited apt-tmpdir cache, installsSIGINT/SIGTERMhandlers that runcleanup_allbefore_exit, and explicitly cleans up on normal exit — closing the per-task/tmp/.private/<user>/zoryn.XXXXXXXX/leak both for completed and interrupted runs. - up (merge):
zoryn up(and--continue) on a branch where the target upstream tag is already merged no longer reports a bogus "Merge failed - conflicts detected". The pipeline now detects that the tag is reachable fromHEADand skips the merge step, letting the remaining stages run. - task manage: when running a task that contains rebuild/delete subtasks (or targets a stable repo), gyle's "please specify a reason" rejection no longer ends the action with an "Error: …" status. The TUI now detects gyle's reason-required errors and re-opens the build-reason input prompt prefilled with the existing task message, preserving the original run mode (default /
--commit/--test-only) so the retry uses the same flags. Also fixes the previously silent path where pressingr(plain run) on a rebuild task would just fail. - gear rules parsing: any
tar.<comp>:directive (where<comp>is a non-empty lowercase-alphanumeric suffix) is now recognised by the.gear/rulesparser, replacing the previous hand-maintained allowlist oftar.gz:/tar.bz2:/tar.xz:. Concretely,tar.zst:was missed: in a package whose.gear/rulesstarted with e.g.tar.zst: thunderbirdfollowed by a secondarytar: l10n name=l10n,find_tar_directoryskipped the first directive andzoryn uptried to update thel10nsubdirectory instead of the main source tree. The four duplicated prefix lists (intar_line_uses_version_template,parse_tar_directory,main_tar_uses_version_template, andrewrite_rules_for_upstream_git) now share a single helper, so future compression suffixes added by gear are picked up automatically rather than producing a silent miss. - up (merge-hooks):
zoryn upon an already-updated project (or--stage merge-hooksafter the fact) no longer feeds the bare spec version into$TAG, which broke hooks doinggit rev-parse "$TAG^{}"withambiguous argument '1.5.0^{}'for upstreams that tag as e.g.v1.5.0(kryoptic). The stage now resolves the spec version to an actual tag viaGau_git.find_all_matching_tags(honoring.gear/rulestag prefixes andpkg_name-patterns), and skips merge-hooks cleanly when no tag matches instead of running with a non-ref string. Closes #78. - task batch: main package no longer fails with
gear-create-tag failed(and a follow-upsrc refspec ... does not match anyon push) when re-running for a second target repo: the per-package tag is now resolved up front and reuses the bare<version>-<release>tag on HEAD when the batch-formatted tag is not present, so push andtask addreference the tag that actually exists. Fresh first runs also create the main package's tag with the batch-formatted name (gear-create-tag -n …) for consistency.
0.38.0 - 2026-05-05¶
Added¶
- task refresh:
--runqueues the task for build right after a successful refresh; pair with--test-only/--commit(mutually exclusive) and-m MESSAGEto forward those flags totask run. - changelog:
web_regexparser for upstream HTML release notes (e.g. Wireshark). Configureparser = "web_regex",url,web-regex-pattern(PCRE with named groups(?<id>...)and(?<desc>...)), and optionalweb-regex-stop-atsubstring marker in.gear/version-up. URL-based parsers (web_regex,markdown,osv-json,html-table) also gain{old_version}/{new_version}placeholder substitution. CVE descriptions are RPM-macro-escaped (%→%%) before being written into the spec%changelog, so upstream prose containing literal%cannot trigger macro expansion.
Removed¶
- config: dropped legacy INI config support — the
zoryn_config_fixmigration utility, the INI parser, and the on-load auto-detection path are gone. INI-style~/.zorynand.gear/version-upnow print a TOML parse warning on stderr and fall back to defaults; INI-style batch configs fail with a parse error. Convert them to TOML manually.
Fixed¶
- task delete/approve/disapprove/log:
pkg.git=tagnow picks the subtask whose tag matches (not the first sharing the git repo); a bare ambiguouspkgerrors out and lists the candidates. Closes #74. - bash completion: TAB after
pkg.git=tagprefix no longer stalls or duplicates thepkg.git=prefix in the reply. - config: invalid TOML in any user-edited config file now fails with
zoryn: <path>:<line>:<col>: invalid TOML: <msg>and exit 1, instead of silently falling back to defaults with only a stderr warning. Previously a typo in.gear/version-upwould silently swallow the entire[changelog]section sozoryn upproduced an entry without CVE references;~/.zoryn,builders.d/*.conf, and the[commands]section had the same silent-fallback hazard.Toml_utils.warn_parse_error(the helper that printed the warning) is replaced byfail_parse_errorthat raisesFailure, caught bymain.ml's top-level handler. Batch config files (which already returnedErrorfrom a result-typed API) keep their result-based behavior; only the redundant stderr warning is dropped there. - tests:
test_cmd_gen_watch's "fails for nonexistent package" case now runs withZORYN_TEST_FIXTURESset, so the binary uses fixture-mode HTTP instead of hitting live Debian/Gentoo/Arch endpoints. Without it the test was flaky on CI — a transparent proxy or stray upstream hit could mark a bogus package as "found" and the expected non-zero exit never came. - up: scheme detection now falls back to
tarball-watchwhen every structural git-merge signal is absent but a.gear/watch(or acopy: *.watchrule /Source: *.watch) is present. Previously the scheme stayedunknownandzoryn upfailed with "no upstream URL found" for packages whose upstream is a plain HTTP tarball site (e.g.dhcp_probefromwww.net.princeton.edu). - up: for tarball-watch packages whose spec
Url:points at PyPI, the version now comes from PyPI and the watch file is consulted only to find the matching tarball download URL. Previously the pipeline searched for a git tag matching the PyPI version (fails without an upstream git remote, e.g.python3-module-grpcio-tools), and the pure-watch fallback was prone to picking upstream pre-release tags like1.80.0-pre1over the real release1.80.0.
0.37.0 - 2026-04-22¶
Security¶
- up (hooks):
zoryn upnow lists every.gear/{up,merge-up}.d/*script by name before the[y/N]prompt when hooks would run unsandboxed (via--no-sandbox,--sandbox direct, a missing runner binary, orrunner=directin~/.zoryn), and strips control bytes and Unicode bidi/zero-width/line-separator/BOM hazards from the displayed names (CVE-2021-42574 "Trojan Source" class). The announcement, rejection notices, and prompt all share stdout so stderr redirection cannot silence part of the picture;--continueno longer inherits--no-sandboxor cached consent across processes.
Changed¶
- up (hooks) — BREAKING: symlinks under
.gear/up.d/and.gear/merge-up.d/(and the hook directories themselves if they are symlinks) are now refused: the target may differ from the displayed name, so a.gear/up.d/innocent -> /bin/shtrick would defeat the new list-before-prompt safeguard. Replace such entries with regular files (copy, include, or use a helper script that invokes the real target).
Fixed¶
- task approve/disapprove all: delegate the
alltarget to gyle in a single SSH call so stale client-side subtask state no longer yieldssubtask #N not foundcascades before the real subtasks are reached. Also warn when--revokeis combined with-m(gyle ignores the message). Closes #68. - task refresh: stop picking stale repo snapshots on girar —
get_last_done_task_idsorted candidate tasks bytask_iddescending and picked the firstDONEone, but girar does not commit tasks intask_idorder (a task with a higher id can finish before one with a lower id). The reference snapshot could therefore predate already-committed newer versions, sotask refreshreported "all packages up to date" even when subtasks were clearly behind repo. Now sort bytask_changeddescending (withtask_iddesc as a tiebreaker) to pick the latest-committedDONEtask. - task manage: no more "ghost symbols" on the subtask-detail and dependencies headers — the colored
#task_id / Sub #sub_id -- STATE -- repo -- owneroverlay on the subtask-detail screen was drawn one column left of the plain header text (" / Sub "is 7 chars, not 6), so each value's trailing char bled through from the uncolored underlay (Sub#1000,try 11,sisyphuss,nickell). The-- Dependencies of #idoverlay was off by one the other way (the prefix is 19, not 20 chars), rendering a doubled##id. Closes #70. - task manage: mouse selection no longer leaks across screen boundaries — clicking on TaskList, then entering the log viewer (or the other way around) used to leave the old
(row, col)selection in place; the log view then re-interpreted it as logical(screen_line, col)and painted reverse-video in the wrong spot, and the idle-release copy grabbed whatever happened to live at those coordinates. Selection overlays and the copy path are now gated on the screen the selection was started on. - task manage: mouse selection is cleared on every data refresh (manual
R/F5, SSE push, auto-poll fallback). The absolute(row, col)coordinates became stale as soon as the task list / detail was reloaded, so the copy-on-release path risked returning text from an unrelated row. - task manage:
Copied: …preview truncation is UTF-8 safe and no longer slices a multi-byte character mid-sequence (the status line usedString.subon byte offsets). - Shell mock layer (tests): SSH commands now require a fixture when
ZORYN_TEST_FIXTURESis set — previously the test harness silently fell back to realsshwhen the fixture file was missing, so tests could accidentally connect to a live build server against the developer's credentials (and pass or fail depending on their real task state). Missing fixtures now raise a clearShell fixture missingerror. Fall-through behaviour is preserved for git/gear/hsh because many tests rely on real local-only git operations on fixture-built repos.
Added¶
- task manage: full mouse text selection on the task list, detail, deps and subtask detail screens — drag to select a range, double-click to select a word. Triple-click on a task list entry's wrapped package block selects all
pkg=versionitems of that task (for other triple-click targets the clicked screen line is selected). Multi-row copies are joined with spaces (no line breaks) so a block of packages lands on a single paste-ready line. Copy fires automatically on mouse release (after ~0.4 s idle) via OSC 52. Single click on a#NNNtoken still selects just the digits and copies the bare number. - docs/site: project website at https://rider.altlinux.team/zoryn/ — a bilingual landing page plus full MkDocs Material documentation (Installation, Quick start, Configuration, Build farm, Hooks, Sandbox, Batch configs, per-command reference). Sources under
docs/{en,ru}/; thisCHANGELOG.mdat the repo root is the single source for the "Changelog" page (symlinked fromdocs/en/changelog.md; RU falls back to EN viamkdocs-static-i18n). - ci:
.forgejo/workflows/deploy-site.yaml— builds the MkDocs site and publishessite/to Forgejo Pages on every push tomastertouchingsite/,docs/,mkdocs.yml,overrides/, ordune-project. The landing's version badge is baked fromdune-projectat build time byoverrides/hooks.py— one source of truth for the displayed version. Closes #28. - up: new
[tarball] subdirconfig field — subdirectory name inside the tarball to extract (passed asgear-update --subdir=<value>). Supports{version}and{name}placeholders, e.g.subdir = "thunderbird-{version}". Validated against shell metacharacters, path separators (/) and directory-traversal values (.,.., or a..prefix) before use so it cannot escape the intended extraction directory. Useful for tarballs with multiple top-level entries like Mozilla source tarballs - up: new
parser = "mozilla"in[changelog]— fetches CVEs from mozilla/foundation-security-advisories for Firefox, Firefox ESR, Thunderbird (override withmozilla-product).
0.36.0 - 2026-04-15¶
Added¶
- task manage: show approval/disapproval status in the task list — aggregated
✓/✗line with usernames between task header and package list - Persistent search history in TUI search inputs (test-rebuild, up, task manage). Use Up/Down (or Ctrl+P/Ctrl+N) to walk previous patterns and Ctrl+R for reverse incremental search, readline-style. History is kept in
~/.local/state/zoryn/<tui>.search.history(max 500 entries per TUI). Closes #61. - build --section: new
bbandbavalues (with long-form aliasespkgbinaryandpkgall) — runrpmbuild -bb --short-circuit/-ba --short-circuitin the existing chroot to repackage only (Processing files→ rpm, or src.rpm + rpm), reusing the%prep/%build/%installartifacts from the previous build. Useful when iterating on%files, packaging macros, or metadata without rerunning the full pipeline throughhsh.
Changed¶
test-rebuildlog-viewer: pressing Enter on an empty search now re-runs the most recent history entry instead of clearing the match highlight.- build (python auto-deps): auto-fix retry now uses
hsh-rebuildto reuse the existing chroot instead of a cleanhshbuild, significantly reducing rebuild time since all BuildRequires are already installed. Falls back to clean build if chroot is unavailable. (#56)
Fixed¶
- task rebuild:
--skip <pkg>now filters the dependency list in--deps-by-pkg/--from-log/default modes as well (previously silently ignored outside--all-subtasks); numeric subtask IDs in--skipstill apply only to--all-subtasksand now emit a warning elsewhere. - up: upstream fetch no longer aborts when a local tag would clobber an upstream tag — the fetch is split into a mandatory branches step (
--no-tags) and a non-fatal tags step. Errors now name the spec file and the real reason instead of the generic "No upstream remote available". - submit: no longer attaches bogus
--depson tasks from a different build chain.Girar.is_repo_fresherused a flatbranch_sorting_orderindex, so p11 (p-chain) was ranked "fresher" than c10f2 (c-chain) andfind_upstream_tasksfed those tasks into--deps. It now consultsAlt_branches.get_build_predecessors, which understands the real chainssisyphus → p11 → p10 → p9 → p8andsisyphus → c10f2 → c9f2. For c10f2 only sisyphus is considered upstream; p11/p10 tasks are ignored. (#65) - build --section install: drop
--nocheckfrom therpmbuildarguments. ALT Linuxrpmbuilddoes not accept--nocheck(it's ahsh-level option), sozoryn build --section installfailed immediately with--nocheck: unknown option. The flag was also redundant:--short-circuittellsrpmbuildto jump straight to the requested stage, so-bi --short-circuitruns only%installand%checkis never reached. - build (python auto-deps): auto-fix now detects when upstream has changed the check dependencies source (e.g., switching from setuptools to hatchling) causing a source's deps to become empty in
pyproject_deps.json. Previously the build would silently succeed with cached chroot packages but fail on fresh builds. Now zoryn aborts with a clear message asking to update the%pyproject_deps_resync_check_*macros in the spec file. (#62)
0.35.0 - 2026-04-09¶
Added¶
- commit: new
zoryn commitcommand — native replacement forgear-commit, extracts changelog from spec file as commit message; supports--spec,-m,--no-edit,--amend,-aflags. By default opens the message in the same editorgit commituses (resolved viagit var GIT_EDITOR). For vim-family editors (vim/vi/nvim/gvim) the prefilled message is piped through stdin and the buffer is renamed via:file, which makes vim mark the buffer as modified — soZZ,:wq,:xon an unchanged prefilled buffer all commit (unlike opening vim directly on a file, where these commands are no-ops on unchanged content). Save/abort detection is by file existence::q!aborts, empty buffer aborts ("commit message is empty"),:cqaborts with non-zero exit. For non-vim editors falls back togit commit -F file --edit, which has standard git semantics. ExportsGEAR_COMMITandGEAR_COMMIT_AMENDenv vars for git hooks compatibility
Changed¶
- task rebuild / batch rebuild: local SRPM lookup is now stricter — only files whose release starts with
altare considered. Previously the legacyHasher.find_srpmaccepted any release suffix, so a directory containing third-party rebuilds (e.g.foo-1.0-1.fc38.src.rpm,foo-1.0-1.mga9.src.rpm, or vendor packages with noaltprefix at all) could be matched by name. After the unification ontoBuilder.find_srpm_in_path(which shares its matcher with the existingBuilder.find_srpmused everywhere else), such files are silently ignored andtask rebuildwill print "No src.rpm found" for them. If you mirror non-ALT SRPMs into your[sources] srpms_path, keep them in a separate directory;task rebuildonly consumes ALT-tagged SRPMs (this matches the behaviourBuilder.find_srpmhas always used for remote builders). Package names are now also validated throughGau_common.Validation.validate_package_namebefore lookup, mirroring the rest of the builder API.
Fixed¶
- task rebuild / batch rebuild: SRPM lookup no longer matches files whose name shares a suffix with the requested package. Previously the matcher in
Hasher.find_srpmused an unanchored regex (Re.eosonly, noRe.bos), so a search forqt6-webenginewould substring-match insidedqt6-webengine-6.10.2-alt0.dde.1.src.rpmand return the wrong file. The duplicate path-based finder has been removed: bothbin/zoryn/cmd_task_rebuild.mlandlib/batch_pipeline/batch_pipeline.mlnow use the newBuilder.find_srpm_in_path, which shares its fully-anchored matcher (^pkg-…$) withBuilder.find_srpm. The "no src.rpm found" warning intask rebuildwas also updated to describe the new pattern (%s-{version}-alt{release}.src.rpm) instead of the old permissive one. - up: state file location is now resolved via
git rev-parse --absolute-git-dirinstead of the hardcoded.git/zoryn-up-state.jsonpath, sozoryn up(and the build pipeline it drives) works inside agit worktree. Previously the top-level.gitin a worktree is a regular file (agitdir:pointer), not a directory, so writing the state file failed withENOTDIR. The new path lives in the per-worktree git directory (.git/worktrees/<name>/zoryn-up-state.json), which is also naturally isolated — two parallelzoryn upruns in two worktrees of the same package no longer collide. (#60) - up: scheme detection on packages whose
.gear/upstream/remotesexists (or whose spec carries only aUrl:to a public forge, noVCS:) no longer requires the maintainer to add the localupstreamremote by hand and no longer takes minutes on repos with hundreds of merge commits.stage_detectnow resolves the upstream URL from.gear/upstream/remotes→ specVcs:→ specUrl:(withGit_url.normalize+is_github_or_gitlab_urlfilter) and fetches BEFORE running scheme detection. The remote-discovery cascade is conservative and never destroys maintainer state: (1) it first walks all local remotes and reuses the one whose canonical URL matches the determined upstream URL — regardless of its name, sovendor/tools/phobos/etc. conventions are honored; (2) failing that, it respects an existingupstreamremote even when its URL differs from the spec (the maintainer may have pointed it at a mirror or fork deliberately); (3) only when neither match exists does itgit remote add upstream <url>from the determined URL. Fetches use--tagswithout--force, so packaging-side tags whose name coincides with upstream tags are preserved. With upstream fetched, scheme detection runs a new structural fast path: pick the upstream tag matching the current spec version (v<v>,<v>,release-<v>,release_<v>,rel-<v>,<pkg>-<v>), VERIFY it actually came from upstream by checking that its commit is reachable fromrefs/remotes/<upstream>/*(so a maintainer-side packaging tag with the same name does not poison the result), and run a singlegit merge-base HEAD <tag>— non-empty result means HEAD shares SHAs with upstream →git-merge; empty means tarball-imported synthetic SHAs →tarball-watch. Tarball imports cannot fake content-hash identity. Falls through to the legacy Tier 1 / Tier 2 cascade when no matching upstream tag exists locally OR when no candidate is verified as upstream.stage_fetchnow reuses the same safe helper as a fallback whenstage_detectcould not determine a URL, and skips the redundant remote-setup step when the URL is already instate.upstream_url. (e.g.microsoft/onnxruntime: scheme detection went from ~30s of slow tag walking to a singlemerge-basecall.) - up: Tier 1 (changelog-history scheme detector) now short-circuits on the first upstream-shaped merge instead of eagerly classifying every merge in the version-bump range — for packages with hundreds of merges between two version bumps and no configured upstream remote, this turns an O(N) walk into O(1) on the common case. The "all merges proven non-upstream → Tarball" branch still requires walking the whole range and remains correct.
- up: scheme detection no longer misclassifies git-merge packages as
tarball-watchwhen the maintainer uses non-standard merge commit messages such asMerge upstream tag 'X'(e.g. oneDNN). The detector now classifies the last update structurally. The primary signal is commit-identity via the upstream remote: a merge is upstream-shaped iff any non-first parent is reachable fromrefs/remotes/<upstream>/*, where the upstream remote is resolved from.gear/upstream/remotes(proper git-config parsing with[remote "name"]section headers, case-insensitive keys,#/;comments), specVcs:/Url:, or a local remote namedupstream/vendor. SHA1 identity is cryptographic — tarball imports cannot fake it. When a remote is configured, its negative answer is authoritative (no fall-through to weaker tag heuristics), so packaging-branch merges with coincidental tags cannot be misclassified. When no remote is configured, the detector falls back to tag-based heuristics (tag pointing at merge commit, tag in parent diff). Tier 1 walks the spec'sVersion:-bump history with--follow --topo-order, verifies ancestry to skip meaningless ranges after rebase, and handles octopus merges.is_github_or_gitlab_urlexplicitly excludes ALT-internal hosts (*.altlinux.org,*.basealt.ru, etc.) so ALT's gitlab-based infra is never misclassified as upstream. Multi-subproject packages (dmd-style with[remote "tools"]+[remote "phobos"]) are handled correctly by matching gear section names against local remote names. - check version-up: spec file lookup now respects
spec:directive in.gear/rules. Previously the command only scanned the repository root and printed "No spec file found, use --from for simulation" for packages whose.speclives inside.gear/(e.g.python3-module-hypothesis). - up: PyPI tag lookup now finds tags whose
.gear/version-uppattern carries a decorative prefix (e.g.hypothesis-python-{major:+}.{minor:+}.{patch:+}) without needing an explicittemplate;[version] filteris still respected. (#57) - gen version-up: when run from
.gear/or any subdirectory of a git repository, write.gear/version-upto the repository root instead of creating a nested.gear/.gear/version-up. The-C/--chdiroption still defines the working directory explicitly. (#59) - builder add:
--hasher-dirwas ignored in--multi-addmode; now supports{hasher_number}template (e.g.,--hasher-dir '/tmp/h_{hasher_number}'). Without placeholder, template is derived automatically with a warning. Priority:--hasher-dir>--based> default~/hasher_{hasher_number} - task add: TAB completion for package names now searches across all branches instead of filtering by the task's target branch — previously e.g.
zoryn task add 411104 copy ocaml-l<TAB>(task for p11) hidocaml-lzmabecause it does not yet exist in p11 - gen pypi2spec: emit
VCS:tag when the repository URL comes only fromhome_page(noproject_urls.Repository), e.g.fasttext. - gen pypi2spec:
--urlnow overrides any URL discovered in PyPI metadata (was: only used as a fallback) and is reflected in the spec'sVCS:line. - gen pypi2spec: normalise PyPI repository URLs through a shared
Gau_common.Git_urlhelper — handlesgit+https:/git:prefixes, trailing slashes, and.gitsuffix uniformly across all code paths.
0.34.0 - 2026-04-06¶
Added¶
- task refresh:
--from <branch>flag for cross-branch sync — detect stale subtasks and replace with copy from source branch - task refresh:
--types <types>flag to control which subtask types are checked (copy, rebuild, build=gear+srpm, ALL) - task test-rebuild: warn about packages already FTBFS on beehive before building; add
--skip-ftbfsto skip per-architecture build tasks where the package is already FTBFS - TAB completion for package names from Repoteka API in
clone,check,task, andgen watchcommands
Fixed¶
- task ls/show: fixed missing package names for delete/copy/rebuild subtasks — used
pkgnamefield from Tasks API instead of absentpackage
Changed¶
- build: unified single-builder and multi-builder batch build code paths — removed ~300 lines of duplicated logic and the first-package special case
0.33.0 - 2026-04-05¶
Added¶
- tui: mouse text selection in log viewer — click+drag to select,
yto copy original text (without wrapping) to clipboard via OSC 52 (#52) - tui:
Space(page down) andb(page up) keys in log viewer — matching standardlesskeybindings (#50) - Syntax highlighting for log output using sublime-syntax engine (#46)
- build:
--sectionflag to run a specific rpmbuild section (prep,build,install,check) in an existing chroot with automatic spec file sync,--rpmbuild-argsfor passing extra rpmbuild arguments — for iterative build development and debugging - build: configurable log filename template
log_filenamein[build]config section (~/.zoryn). Supported variables:{builder},{batch},{pkgname}. Default:build.{batch}.{builder}.log - build, up:
--skip-checknow supportsrpmbuildvalue to skip%checksection via--rpmbuild-args='--without=check'; bare--skip-checkwithout value skips everything (rpmbuild + post-build checks) (#45) - gen pypi2spec:
%pyproject_runtimedeps_metadatafor upstream runtime dependencies (#48) - gen pypi2spec:
AutoReq: yes, nopython3to disable python3 autoreq (#48) - gen pypi2spec:
%global _unpackaged_files_terminate_build 1in generated spec (#48)
Changed¶
- tui: log viewer line wrapping is now character-based instead of word-based — better for logs with long strings without spaces (#54)
- build: default log filename format:
build-log.{builder}->build.{batch}.{builder}.log - build: removed separate
.errlog files for parallel builds — stderr is now written to the main log - gen pypi2spec: spec filename changed from
python3-module-<name>.specto<normalized-pypi-name>.spec(e.g..gear/hdbscan.spec) (#48) - gen pypi2spec:
Url:tag now always useshttps://pypi.org/project/<name>/instead of homepage from metadata (#48) - gen pypi2spec: removed
%docline from%files— don't package docs and license files (#48)
Fixed¶
- tui: last character lost when wrapping long lines in log viewer — byte offset for syntax highlighting was calculated incorrectly due to a missing space at the wrap boundary (#53)
- build: show SKIP (yellow) instead of FAIL (red) when architecture is excluded in spec file (
ExcludeArch) — detected from build log (error: Architecture is excluded: ...) (#49) - gen pypi2spec: add
%define pypi_nameto generated spec and use%{pyproject_distinfo %pypi_name}instead of%pyproject_distinfo %name— fixes incorrect dist-info path matching (#44)
0.32.0 - 2026-03-31¶
Added¶
- submit: automatically squash duplicate release commits — when re-running
zoryn submitafter changes, if the previous commit has the same version-release subject (e.g.6.151.10-alt1), amends it instead of creating a duplicate - PyPI version source: for Python packages with
URL: https://pypi.org/project/<name>/in spec,zoryn upandzoryn check versionnow use PyPI as the source of truth for version numbers instead of git tags — prevents updating to yanked (withdrawn) versions; PEP 440 normalization handles version format differences between PyPI and git tags; use--tagto bypass PyPI detection - task test-rebuild --top: full TUI rewrite using LTerm — no more screen flickering, syntax-highlighted build logs with search (
/,n/N), full-screen log viewer with follow-mode, build log list (l) showing all results, help screen (F1), hotkeys synced withtask manage - tui_logview: new shared library for log viewer logic (classifier, word wrap, caching, search) — used by both
task manageandtask test-rebuild zoryn task abortcommand — abort a running task by ID (with TAB completion for task IDs)zoryn task approve/zoryn task disapprovecommands — approve or disapprove task subtasks from CLI (by subtask number, package name, orall), with optional-mmessage and--revoke- Changelog CVE parser: support
CVE:YYYY-NNNNNformat (used by ISC Kea/BIND) — normalized to standardCVE-YYYY-NNNNN - Changelog version header: support
Product X.Y.Z (status) released on Dateformat (ISC ChangeLog) - Tests for
Io.read_package_list_file(path traversal, comments, blank lines, errors) - Tests for
Custom_packages.collect(combined--package/--packages-filelogic) - Tests for
List_utils.chunkwithsize <= 0 - Test for
Stats.create ~custom_packages:[]normalization toNone - Extract
Custom_packages.collectintotest_rebuildlibrary for testability - task test-rebuild:
--package PKG(repeatable) and--packages-file FILEflags to rebuild a custom set of packages instead of the full RDB dependency tree
Changed¶
- task manage: log viewer logic extracted to shared
tui_logviewlibrary (no user-visible change) - task test-rebuild --top: event loop migrated from
Unix.selectto Lwt for LTerm integration
Fixed¶
- gen version-up: tags with package name prefix and v-prefixed version (e.g.
--tag freeipmi-1-6-17 --to-version v1.6.17) generated wrong pattern with 4 groups instead of recognizingfreeipmi-as prefix — now stripsv/Vprefix before matching - task test-rebuild --top: pressing
[f]to view failures on the completion screen did nothing —renderalways showed the completion box ignoringview_mode, and the post-completion input loop never re-rendered - task test-rebuild --top: pressing Enter on failures without log files (e.g. "SRPM not found") did nothing — now shows the error message inline with a "press any key" prompt
- package_spec:
is_alt_releasenow accepts branch releases (alt1.M110P.1) and architecture-specific releases (alt1_e2k1) — previously only digits and dots were allowed afteralt, causing release to be merged into version and breaking version comparison - task test-rebuild:
--continue-no-refreshnow updatespackages_to_rebuildin stats when package list changes between runs, preventing stale diff comparisons on subsequent--continue - task manage: approve/disapprove with message and run-with-commit message failed in TUI —
interactive_quietmode appended</dev/nullwhich overrode theecho msg | sshpipe task test-rebuild: fix incorrect rebuild of task's own packages when subtasks use the same git repo with different tags (e.g.timescaledb.gitproducingpostgresql15-timescaledb,postgresql16-timescaledb, etc.) — now filters by APIdepthfield instead of fragile name matchingrdb:parse_task_packages_jsonnow usessrc_pkg_namefield from API whensubtask_packageandsubtask_srpm_nameare empty, giving correct package names for gear subtasks with multiple packages from one git repo- task test-rebuild:
--packages-filewith only comments/blank lines no longer rejects valid--packagearguments (C1) - task test-rebuild:
--continue-no-refreshnow correctly preservescustom_packagesin stats, preventing false mode-switch detection on subsequent--continue(C2) - task test-rebuild: validate package names before sending RDB requests — reject names with invalid characters early
- task test-rebuild: remove duplicate "Warning:" prefix in
print_warningmessages List_utils.chunk: raiseInvalid_argumentonsize <= 0instead of producing incorrect resultsList_utils.take: rewrite as tail-recursive to prevent stack overflow on large listsrdb_packages.mli: fix reference to non-existentvalidate_src_package_name(nowvalidate_package_name)
Changed¶
test_rdb_packages.ml: replaceList.assocwithList.assoc_opt+ proper error messagesList_utils.take/chunk: improve documentation in.mli
0.31.0 - 2026-03-24¶
Added¶
- Automatic
pyproject_deps.jsonupdate and rebuild retry when Python package dependencies change during build (--no-python-auto-depsto disable) zoryn gen pypi2speccommand: generate ALT Linux RPM spec files for Python packages from PyPI metadatatask ls: task IDs are now clickable hyperlinks (OSC 8) topackages.altlinux.org/tasks/{id}/— works through SSH in supported terminals (Konsole, GNOME Terminal, foot, iTerm2, Windows Terminal)- task manage: real-time updates via SSE (Server-Sent Events) from the task API — tasks are updated instantly when their state changes instead of polling every 15 seconds
- task manage: SSE connection indicator
[SSE]in status bar - task manage: automatic reconnection with exponential backoff when SSE connection drops, with polling fallback during reconnect
task manage:c(commit) andt(test-only) actions in the actions menu for running tasks with--commit/--test-onlyflagstask manage:copy pkg [repo]support for adding copy subtasks (with TAB completion)task manage: pre-fill commit message with existing task message when running with--committask manage: includecopysubtask type in commit message prompt check (alongsiderebuildanddelete)task logcommand — direct TUI log viewer for build and event logs (subtask by number or package name with TAB completion)zoryn task batch --continue— resume interrupted batch from last checkpointzoryn task batch --abort— discard interrupted batch statezoryn task batch --restart— discard old state and start fresh- Pipeline framework:
save_intermediate_statefor mid-stage state persistence - builder config: per-branch default builders via TOML sub-tables (
[builders.p11],[builders.p10])
Fixed¶
- task test-rebuild: now respects
[builders] defaultconfig setting — previously always fell back to local builder (#39) - up: don't abort merge when unresolved conflicts remain — leave merge in progress so the user can resolve conflicts manually and run
zoryn up --continue - up (tarball-watch): fix watch file parser including uscan action keywords (
debian uupdateetc.) in the regex pattern — only the second field is the pattern, remaining fields are ignored - up (tarball-watch): fix version detection when page hrefs use a different scheme (http vs https) than the watch file URL — now tracks HTTP redirects and builds additional patterns like uscan's
handle_redirection - up: add
WATCH:debug output for watch file version fetching (hrefs, regex patterns, effective URL) - up: enable
Gau_http.debug_modewhen--debugflag is set (was missing, no HTTP debug output) - http: handle servers with non-standard
Content-Encoding(e.g.x-identity) — retry withoutAccept-EncodingonCURLE_BAD_CONTENT_ENCODING; file downloads skip encoding entirely
0.30.0 - 2026-03-18¶
Added¶
task manage: event log viewer (lkey on task detail screen)task manage: word wrap for long lines in log viewertask manage:--no-colorflag andNO_COLORenv variable to disable colored outputtask manage: per-part coloring in task list (yellow ID, colored state, bold-blue repo, sand owner, dim date) matchingtask lscolor schemetask manage: subtask search in task detail (/,s,F7) withn/Nfor next/prev matchzoryn up --continue: resume pipeline from the last failed stagezoryn up --abort: rollback changes and discard pipeline statezoryn up --restart: discard saved state and start a fresh pipeline runzoryn up --stage=<STAGE>: run a single stage in isolation (for debugging)zoryn up --from=<STAGE>: run pipeline starting from a specific stage- New
lib/pipeline/library: generic sequential pipeline with state persistence - task test-rebuild --verify-failures: rebuild failed packages without task RPMs to distinguish regressions (caused by task) from pre-existing failures
- task test-rebuild --verify-failures: separate verification logs with
verify.prefix (verify.pkg.arch.log) - task test-rebuild --verify-failures:
verification_summaryandverification_by_archfields in JSON output - task test-rebuild --verify-failures:
--continueintegration for resuming interrupted verification phase - task test-rebuild: graceful Ctrl+C and TUI quit (
q) handling — stops entire process instead of advancing to verification phase - task manage: full-text search across subtasks (
/,s,F7) via taskoteka APIqparameter - task manage: build log viewer — navigate architectures on subtask detail (log + srpm.log), Enter to download and view log with scrolling and
/search - task manage: CLI options
--user,--state,--repo,--needs-approvalfor initial filters; positionalTASK_IDto open task directly
Changed¶
- up:
--skip-checkvalue now preserved asstring optionthrough pipeline state (was lossybool) - up:
update_packageAPI no longer runs build/batch-build stages (callers handle builds separately)
Fixed¶
- sandbox: fix merge hook env vars (TAG, COMMIT, SHORTCOMMIT) treated as commands in
execute_directandexecute_hasher— env var names must not be shell-escaped - pipeline: fix lock file descriptor leak —
runandcontinuenow release lock viaFun.protect ~finally - pipeline: replace
List.hdwith propermatchfor empty list safety - up:
--fromno longer blocked by existing state file; uses saved state when available - up:
--sandbox=RUNNERnow correctly passed to pipeline state (sandbox_runner_overridefield) - up:
--rebuildflag now applied in pipeline build stages (was silently ignored) - up:
batch_buildstage now propagatesresolve_batch_opterrors instead of silently returning None - up:
build_changelog_messagenow passes~on_debugtoChangelog.build_message - up:
detect_statenow returnsErrorwhen spec file not found (was returning empty string) - up: add desktop notifications on pipeline success/failure
- up: add summary output and "next steps" after successful update
- up: set terminal title to package name during update
- pipeline/dune: remove unused
gau_commondependency - clone: show git clone progress instead of suppressing output with
-q - build / up: post-build package checks (
sisyphus_check) now only check RPMs from the current build, not all packages accumulated in the hasher repo — filters by build log "Wrote:" lines; for remote builders, uses downloaded RPMs dir instead of local hasher which may contain stale packages - build: fix "Wrote:" line parsing in build logs failing when rpmbuild appends compression suffix like
(w2T16.xzdio)after.rpm
0.29.0 - 2026-03-15¶
Fixed¶
- build: fix rebuild fallback checking wrong log filename
- cli: write
print_errorandprint_warningto stderr - clone: wrap
Sys.chdirinFun.protectin init-gitery path - gau_http: fix IPv6 link-local SSRF check false positives
- girar: validate remote name with git rules, guard empty
pkg_name - pty: fork safety and buffer cleanup in PTY operations
- rdb: split version/release on last dash, not first
- sandbox: escape environment variable keys in shell commands
- spec: case-insensitive Version/Release tag matching in writes and reads
- spec:
detect_old_versiononly searches after%changelog - spec: expand RPM macros in
get_vcs_tag, stop at preamble - spec-check: skip URL/VCS reachability checks for unexpanded RPM macros
- ssh_mux: only register host when SSH master start succeeds
- tui: prevent crash on narrow terminals
Changed¶
- sandbox: suppress noisy hasher chroot preparation output; show compact stage progress with timing instead (use
--verbosefor full output) - build_log: extracted build stage detection into
build_loglibrary, removing code duplication betweencmd_buildand TUI - build_log: replaced nested if-then-else chains with declarative pattern matching for build stage detection
0.28.0 - 2026-03-12¶
Fixed¶
- task copy: use SRPM package names (from Tasks API
pkgnamefield) instead of git repository names — fixes "package already queued" errors when a single git repo (e.g.dune.git) produces multiple source packages (dune, dune-ordering, dune-dyn, etc.) - task copy: switch from Girar SSH + RDB to Tasks API for fetching source task data — faster and returns correct
pkgname,version,release,epochfields - task copy: use batch RDB query for EVR comparison — fetches all target repository packages in 1-2 HTTP requests instead of one per rebuild subtask (avoids rate limits on large tasks)
- spec changelog / up: fix CVE sub-item lines getting
-prefix instead ofindent —normalize_changelog_linesnow preserves compact continuation (+ (fixes:/* (fixes:) and extended (* CVE-...:) sub-item patterns - spec changelog / up: auto-detect extended CVE format (
- Fixes:/* CVE-...: description) from existing changelog, in addition to compact continuation with+or*markers — detected style is preserved when generating new entries - spec changelog / up: replace
charsub-marker parameter with[ \Plus | `Star ]variant type — prevents invalid marker characters; detect existing marker whencve_format = compact_continuationis explicitly configured; skip header lines and pre-header garbage indetect_cve_style` sub-item scan - task ls: show warning to stderr when Tasks API is unavailable (instead of failing silently)
- up --switch-to-upstream-git: always run
gear-update-tag -a -cafter migration — previously skipped when.gear/tags/listdidn't exist, causing build failure
Added¶
- task copy: add
--copy-rebuildsand--rebuild-onlyflags — control how rebuild subtasks with EVR differences are handled; without flags, interactively asks per package whether to copy from source or rebuild from target - task copy: add
--dry-run/-nflag — preview which subtasks will be copied, rebuilt, or deleted without creating a task - task_api: add
version,release,epochfields tosubtask_summarytype - task_api: add
get_taskfunction — returns parsedtask_summaryfor a single task ID - task_api: export
parse_task_summaryfor testing - task manage: interactive full-screen TUI for browsing and managing tasks — mutt-style interface with task list, detail view, dependency management; supports approve/disapprove, run/abort, share toggle, inheritance checks; auto-refresh, filters, keyboard navigation
- task manage: filter dialog overlay (
fkey) — select State/Repo/User from picker lists with incremental search, cycle Approval with arrow keys; replaces sequential text input - task ls: add
--all/-aflag as alias for--user ALLto show all users' tasks
0.27.0 - 2026-03-10¶
Fixed¶
- task test-rebuild, task rebuild: derive branch from task's
task_repoinstead of hardcoding tosisyphus— builders and RDB dependency queries now correctly match the task's target branch (e.g.p11); warn on unknown branches; error on empty branch
Added¶
- task_api library — HTTP client for Tasks API (faster alternative to SSH)
- task ls, task show: use Tasks API by default with automatic SSH fallback
- task rm: TAB completion for task IDs via Tasks API
- task ls: TAB completion for
--uservia Tasks API/ownersendpoint - task add: add subtask to task — supports
repo,del,rebuild,copyactions andpkg.git=tagshorthand; compatible withssh gyle task addsyntax; TAB completion for task IDs - task run: queue a task for build — supports
--test-only,--commit,--dry-run,--fail-early,--fail-late,--hurry,--unhurry,-m(multi-word message in quotes, or-m -for stdin); optional task ID (uses latest if omitted); TAB completion for task IDs - task ls:
--needs-approvalfilter — show tasks needing approval frommaintortester(works with both API and SSH fallback) - task show: new header fields —
swift=yes/no,aborted_by=NAME,depends=ID,ID,... - task show: new subtask fields from Taskoteka API —
tag_id,tag_author,fetched,rebuild_from,copy_repo,spec_type,approved_by,disapproved_by,relax_git_inheritance_check_for_commit,relax_lastchange_inheritance_check_for_evr - task delsub: alias for
task delete(compatible with gyle)
0.26.0 - 2026-03-09¶
Added¶
- task rm: remove a task from gyle
- task show: show task details with colorized output in default, brief (
--brief), and JSON (--json) modes; uses SSH multiplexing - task ls: list build tasks with colored state output and filters (
--branch/-B,--state,--package/-p,--test-only,--user); comma-separated values for--branchand--state(e.g.--state FAILED,EPERM);--repoalias for--branch; new task states: COMMITTING, FAILING, POSTPONED, SWEPT; streaming output (lines displayed as they arrive from SSH) - ssh: SSH multiplexing for girar and gau_git — all SSH connections to gyle/gitery (task operations, git push/fetch/ls-remote) now reuse multiplexed connections via ControlMaster
- build, submit, up:
--batch-pkgsflag to filter batch values — build/submit/update only specified packages from[batch]section instead of all values - submit --with: subtask positioning —
--with ^pkg(before),--with pkg:(after),--with TASK_ID,--with TASK_ID:ref(after ref),--with TASK_ID^ref(before ref); ref can be a package name, subtask number, orpkg.git=tag - changelog: Oracle CSAF JSON advisory parser — automatically extracts CVEs from Oracle Critical Patch Update advisories in CSAF format. Configure with
parser = "oracle-csaf"in[changelog]section of.gear/version-up - gau_http: per-call
max_body_sizeparameter forfetch_url,fetch_url_opt, andfetch_url_result— allows overriding the globalmax_response_sizelimit on a per-request basis - compact_continuation CVE format — new
compact_continuationCVE format for changelog entries that places CVEs on separate continuation lines grouped by 4 (configurable viacves_per_line):+ (fixes: CVE-1, CVE-2, CVE-3, CVE-4). Auto-detected from existing spec changelog style viadetect_cve_style. - task test-rebuild: record
builders_to_rebuildinrebuild-stats.json— the list of builder names passed at launch is now persisted in the stats file; updated on--continue - changelog:
osv-packagenow accepts multiple comma-separated package names or a TOML array — each package is queried separately and CVE results are merged with deduplication. Example for Go:osv-package = "stdlib,toolchain"orosv-package = ["stdlib", "toolchain"] - check version-up: validate
.gear/version-upconfig — validates[version],[changelog],[merge],[batch],[tarball]sections for invalid values, missing required fields, and unused keys; checks all sections (including[sandbox],[add_changelog]) for unknown keys and unknown parser/format values. Optionally simulates changelog generation with--from/--toversion range. Use--no-networkto skip network checks.
Security¶
- up — prompt for confirmation before running hooks without sandbox isolation when configured runner is unavailable. In non-interactive mode, execution is refused. Use
--no-sandboxto run hooks without sandboxing (no prompt).
Fixed¶
- submit — spec check now correctly reports wrong changelog dates as errors (not warnings) when only the release changes. Previously,
make_entry_severitycompared only version against RDB, so a release-only bump matched the RDB entry at position 0, downgrading all diagnostics to warnings. - up — fixed
--tagversion extraction for packages likemysql-8.0.45:Version.normalizewas replacing all dashes with dots, turning tag name intomysql.8.0.45. Now strips known prefixes (gear tag prefix orpkg_name-) before falling back to normalize.
0.25.0 - 2026-03-03¶
Added¶
up --switch-to-upstream-git: migrate a tarball-based package repo to the upstream-git scheme — moves spec and altlinux files (patches, configs, etc.) to
.gear/, rewrites.gear/rulestotar: <tag>:.format, addsupstreamremote, then merges with--allow-unrelated-histories; upstream URL is read fromVCS:tag; use--tagto specify the upstream tag when auto-detection fails; creates.gear/if it does not exist yet; rejects already-migrated repos; validates--tagagainst fetched tags; detects altlinux subdirectory layout correctly including nested paths and mixed root-level files; root-levelpatch:entries are moved to.gear/even when copy files live in a subdir;git+ssh://URLs are preserved as-is;?glob metacharacter is expanded in copy/patch pathsup:
[merge] schemeoverride in.gear/version-up— setscheme = "git-merge"orscheme = "tarball"to skip auto-detection- up --switch-to-upstream-git: automatically writes
scheme = "git-merge"to.gear/version-upafter successful migration - config:
Config.set_value_preserving— line-level TOML editing that preserves comments and key ordering (unlikeset_valuewhich re-serializes)
Changed¶
up --switch-to-upstream-git: now continues with build after migration completes; if the merged tag is newer than the current spec version, Version/Release/%changelog are updated first
Fixed¶
- gen watch: fix Debian watch file fetch for non-main areas — packages in
non-free,non-free-firmware,contribetc. were always searched underdata/main/resulting in 404; now the area is taken from the Debian Sources API response - gen watch:
--debian-pkg,--gentoo-pkg,--arch-pkgoverrides now serve as cross-source fallbacks — when Gentoo or Arch search fails with the ALT package name, names from other overrides are tried automatically; override names are shown in the output - gen watch: add debug logging (
-d) for all search steps — shows Debian Sources API queries, Repology lookups, watch file fetch URLs, and fallback attempts - up: implement
searchmode=plainfor watch files — previously the option was parsed but ignored; version matching always searched only in HTML href attributes, breaking watch files that use JSON APIs (e.g. GitHub Releases API) or plain text listings - changelog: parse release-note headers like "Valkey 9.0.3 - February 23, 2026" (Valkey project) without false positives on prose sentences
- task rebuild: download and sync RPMs from build task to hasher before rebuilding dependent packages — previously packages were rebuilt against stale versions already in hasher instead of the new ones from the task
- ssh mux: clean up stale socket files when SSH master is dead —
ensure_masternow tries-O exitand removes orphaned sockets before starting a new master - ssh mux: add
ServerAliveInterval=15andServerAliveCountMax=3to master connections — detects dead TCP connections within ~45 seconds instead of waiting for ControlPersist expiry; configurable via[ssh] alive_intervalandalive_count_maxin~/.zoryn - builder clean: cleanup commands now use SSH multiplexing — previously each cleanup ran a separate SSH handshake, now they reuse the ControlMaster connection
- builder clean: filter SSH mux errors from stderr —
mux_client_request_sessionandControlSocket already existsmessages no longer appear when MaxSessions is exceeded; SSH still falls back to direct connections automatically
0.24.0 - 2026-03-02¶
Added¶
- Desktop notifications for long-running commands (build, up, task rebuild, task test-rebuild, task batch) — sends OSC 99 escape codes followed by BEL; shows popup + bell in kitty, WezTerm, foot; bell only in other terminals; can be disabled via
[notify] enabled = falsein~/.zoryn - SSH connection multiplexing for builder operations (ControlMaster) — reuses TCP connections to the same host, reducing latency for multi-step operations
- New
[ssh]config section in~/.zoryn:multiplexing(default: true),persist(default: 2m) - spec add changelog: duplicate version-release detection —
add_changelog_entrynow checks for existing entries with the same version-release: appends and deduplicates lines if the packager matches, returns an error on packager conflict
Fixed¶
up: handle
add_changelog_entryerrors in batch update path — previously the result was silently discarded withlet _ = ..., swallowing packager conflict errorswatch: fix parsing of unquoted
opts=with regex patterns containing(?:...)— the colon in non-capturing groups was misidentified as a version=5 key-value separator- watch: expand
@ARCHIVE_EXT@,@ANY_VERSION@,@PACKAGE@and other macros before version matching infetch_versions— previously macros were left as literal text in the regex, causing no versions to be found - watch: expand all Debian macros (
@PACKAGE@,@ANY_VERSION@,@ARCHIVE_EXT@,@SIGNATURE_EXT@,@DEB_EXT@,@SEMANTIC_VERSION@,@STABLE_VERSION@,@COMPONENT@) during watch file parsing — matches uscan behavior where macros are expanded at WatchFile level before line parsing, ensuring all downstream consumers (URL building, mangle rules, pattern matching) receive expanded values - up: commit only the source directory after
gear-updateinstead ofgit add -A— previously unrelated files (e.g..gear/build-log.*) were accidentally staged and committed
0.23.0 - 2026-03-01¶
Added¶
- build: pre-flight mountpoint validation — reads
allowed_mountpointsandallowed_devicesfrom hasher-priv config files (instead ofhsh --printenv) to check that all mountpoints required by the build command are permitted; stops with an error and fix instructions before building if entries are missing - builder add: auto-fix mountpoints via sudo during builder creation — if
allowed_mountpointsin/etc/hasher-priv/systemis missing required entries, attempts to fix automatically - builder: pattern selectors for
-bflag —@all(all builders),@host:host(by host),@host:host[N-M](host range),name[N-M](name range), and^exclusions (e.g.@all,^local,^@host:basalt,@all)
Changed¶
- builder: removed
--all-buildersflag — use-b @allinstead (can be combined with--arch/--branchfor filtering)
Fixed¶
- builder status: limit concurrent SSH connections per host (max 7) to prevent SSH
MaxStartupsrejections when checking many builders on the same remote host - spec check: fall back to
git ls-remotewhen VCS URL HTTP check fails (fixes false positives for git servers like SourceForge that return 404 for plain HTTP) - http: enable automatic decompression (gzip/deflate) for HTTP responses, fixing watch file parsing on servers that return compressed content unconditionally
- task test-rebuild: fix SRPM lookup on remote builders (search via SSH instead of local directory scan) and skip rsync upload when the SRPM is already on the remote host
- improved security
0.22.0 - 2026-02-26¶
Added¶
- builder add: add
--multi-add Nflag for mass creation of builders - builder add: add
--start-number Mfor custom starting hasher number - builder add: add
--dry-runto preview builder creation - builder add: add
--forceto overwrite existing builder configs - builder add: support multiple repos via repeated
--repowith auto-detected branches from release files:--repo /srv/repo/p11 --repo /srv/repo/sisyphus - builder add: interactive mode for
--multi-add(activated when--nameis omitted) — prompts for base name, type, host, architecture, and repositories (branches auto-detected) - builder add:
--multi-addcreates hasher users for each builder by default (use--no-create-hasher-usersto skip) - builder add: early prerequisite check (sudo, hasher, hasher-priv) right after SSH connection is established — warns about missing packages and checks sudo NOPASSWD configuration for hasher-useradd before proceeding with interactive prompts
- up — scan upstream changelog for CVEs in tarball workflow. Previously only the git-merge workflow used the
[changelog] filesetting from.gear/version-up; tarball-watch updates now also extract CVEs and include them in the spec changelog entry. - spec add changelog --auto — extract CVEs from upstream changelog when
[changelog]source is configured in.gear/version-up, using the same logic aszoryn up. - up — OSV API integration for CVE extraction (
parser = osv-api). Supports any project tracked by OSV — configureosv-packagein.gear/version-upto automatically find CVEs fixed between versions (e.g., Wireshark, SQLite, etc.)
Changed¶
- ppx_shell_check — detect shell injection in
Unix.open_process_*calls and aliased Shell.run functions (e.g.let run_cmd = Shell.run). Add[@shell_safe]attribute to suppress false positives on pre-validated call sites. Also check all arguments (including labeled) for shell functions likerun_with_callback. - builder add: removed
-B/--branchflag — branches are now auto-detected from repository release files. For--multi-add, use repeated--repoinstead of-B. For single-add, the branch is auto-detected from--repoor prompted interactively. - changelog — consolidated CVE extraction into
Changelog.build_messagewith~on_progress,~on_warning, and~on_debugcallbacks, eliminating duplicated extraction code acrosscmd_up.mlandcmd_spec.ml. Missing changelog files are reported via progress and skipped. Debug mode (-d) now logs source type, file existence, CVE count, and final message for CVE extraction. - version_mapper — added
resolve_changelog_paramshelper to resolve(project_config, source, cve_format)from config, shared bycmd_up.mlandcmd_spec.ml.
Fixed¶
- task copy — correctly replicate subtask types (rebuild, delete, copy) instead of assuming all subtasks are builds. Previously rebuild/delete/copy subtasks were silently dropped because they lack a git tag.
- task copy — use
copyfrom source repo when source task is DONE (copies pre-built binaries instead of rebuilding from tag). - check spec — fix false positive for CVEs in
- Fixes:block when entries use* CVE-...: descriptionformat (colon after identifier). The trailing colon caused the parser to reject the identifier as malformed. - rpm_spec —
get_changelog_betweennow uses RPM version comparison instead of lexicographic string comparison. Previously"9.0" > "10.0"evaluated to true, which could miss or incorrectly include security-relevant changelog entries. - up — avoid false
git-mergescheme detection for packages with module branch merges (e.g. nginx). Tighten directory-level merge patterns to only matchMerge tag '...'andMerge branch 'upstream...', ignoring packaging branch merges likeMerge branch 'nginx-zip-module' into sisyphus. - up — strip
vprefix from version-config template output before writing to spec file. Previously--tag v1.58.1with templatev{major}.{minor}.{patch}wroteVersion: v1.58.1instead of1.58.1, also causingvv1.58.1in gear-update-tag. - up — do not apply version-config filter when searching for the current version's tag. The filter now only restricts which newer versions to consider, fixing "Cannot find any tags for current version" errors on packages with version filters.
0.21.1 - 2026-02-24¶
Fixed¶
- up — fix doubled prefix in alias tags when version-up template already includes the gear-rules prefix (e.g.
vv1.00instead ofv1.00). - watch — fix relative URL resolution (
resolve_href) to follow RFC 3986: strip last path segment instead of appending/. Fixes doubled path in download URLs for watch files pointing at download scripts (e.g.download.php). - watch — use server-provided
Content-Dispositionheader for downloaded file naming, sogear-updatecan correctly detect the archive type. Falls back to URL path when the header is absent. - watch — rewrite href matching to be uscan-compatible: pattern is now
^(?:(?:SITE)?DIR)?FILEPATTERN$with optional site+dir prefix, matching against both raw href and canonical URL. Removes broken$anchor frombuild_filename_regexandstrip_url_queryworkaround.
0.21.0 - 2026-02-24¶
Added¶
- builder — pass
--packager={packager}in hsh build commands, so builders no longer require~/.hasher/configto be configured. Packager is read from[build] packagerin~/.zoryn(with per-builder override inbuilders.d/), falling back to~/.rpmmacrosand git config. - gen environment — saves packager to
[build]section in~/.zorynand includes--packager={packager}in the default build command.
Fixed¶
- builder — fix local builder status always showing "free" when
hasher_dirstarts with~. The$HOMEvariable was escaped in the lockf check command, preventing path expansion. Also add "locked" fallback to the workdir lock check (both local and remote) when no pid file exists.
Changed¶
- build/up/task-rebuild — waiting for busy builders is now the default behavior (previously required
--wait/-w). Use--fail-if-busyto exit immediately when a builder is busy. - build — when builder is auto-selected (by
--arch/--branch) and is busy, all matching builders are polled until one becomes free.
0.20.0 - 2026-02-23¶
Added¶
- version-up — support optional groups
(?:...)?in patterns and templates, so one pattern can match tags with and without optional components (e.g.,v4.18andv4.18_02). Uses standard PCRE non-capturing group syntax.
Fixed¶
- watch —
@ANY_VERSION@macro expansion now closer to uscan: added optional[-_]?prefix (from uscan) and[Vv]?prefix (extension for v-prefixed versions), broadened version character class to include letters (1.0rc1,2.0-beta) and tilde (1.0~git). Previouslybuild_filename_regexonly matched digits and dots; now all code paths use the same character class. - watch — version matching in
fetch_versions_from_pagenow uses anchored pattern matching like uscan: the watch pattern is prepended with the escaped base URL (split into site + directory) and anchored with^...$, then matched against both the raw href and the resolved (canonical) URL. Falls back to unanchored matching when anchored matching fails (e.g., for patterns with.*prefix). Prevents false positive substring matches.
Changed¶
- spec check — printable_fields diagnostics now report exact line and column number for each non-printable byte. Changelog entries use entry-based severity: old (legacy) entries produce warnings, fresh entries produce errors.
- submit --replace — extended syntax:
--replace=TASK_ID:Nspecifies subtask number directly (replaces removed--subtaskflag). Auto-search mode now shows interactive confirmation before making changes. - submit --replace — subtask list now shows package name alongside version tag (e.g.,
subtask 100: ocaml-atd 2.16.0-alt1). When there are many subtasks, shows the first subtask, then..., then 2 neighbors around the match, with...for remaining entries. - rpm_spec — unified
parse_changelog_headerandparse_changelog_dateinto a singleparse_changelog_headerreturningchangelog_header_inforecord with date, packager, epoch, version, and release fields.parse_changelog_dateremoved. - rpm_spec —
parse_changelog_headernow supports non-ALT release suffixes (e.g.3mdk,1.fc35), double-spaced days (Nov 7), and headers without version-release. Duplicate changelog version check skips entries without version.
Removed¶
- submit --subtask — absorbed into
--replace=TASK_ID:Nsyntax.
Added¶
- spec check — added non-ASCII/unprintable character detection in spec fields (Name, Version, Release, Summary, Group, License, Packager, Description, Changelog) across all subpackages. Catches issues that sisyphus_check would reject, with detailed diagnostics showing hex bytes and Unicode character names (e.g.,
0xc2 0xa0 (U+00A0 NO-BREAK SPACE)). - spec check — locale-tagged fields (
Summary(ru_RU.UTF-8),%description -l ru_RU.UTF-8) now allow valid printable Unicode characters (Cyrillic, accented Latin, CJK, etc.), while still flagging control chars, NBSP, and invalid UTF-8. - spec add changelog — duplicate version-release detection: when the changelog already contains an entry with the same version-release, the command appends to the existing entry (same packager) or reports an error (different packager). Running with the same message twice is a no-op.
Changed¶
- rpm_spec —
add_changelog_entrynow returns(changelog_action, string) resultinstead ofbool. Callers get structured information about what happened (inserted, appended, skipped) or an error message. No morePrintf.printf/eprintfin library code.
Fixed¶
- rpm_spec — fixed TOCTOU race in
add_changelog_entry: the file was read twice (once for lines, once for entries parsing). Now reads the file once and parses everything from the same data.
0.19.0 - 2026-02-17¶
Added¶
- spec check — added changelog date validation: detects wrong day of week (e.g., "Mon" when the date is actually a Tuesday) and invalid dates (e.g., Feb 30). Severity is RDB-based (Error for fresh entries, Warning for old).
- builder list — added
--host,--arch,--branchfilter options. Each is repeatable and supports comma-separated values (e.g.,--arch=x86_64,aarch64). Use--host localto show only local builders. Filters combine with--simple.
Fixed¶
- builder clean — fixed deadlock on remote builders where
builtin lockfheld an exclusive lock on the hasher directory whilehsh-rmchroottried to acquire the same lock, causing both processes to hang indefinitely. Lock checks now run in subshells so the lock is released before cleanup commands execute. - up — batch builds (
[batch]in.gear/version-up) now use the same builder settings (parallel mode, verbose flag) as the initial build instead of hardcodingverbose:trueand building sequentially on each builder. - up — batch build logs now include the batch value in the filename (e.g.,
build-log.chrome-trace.localinstead ofbuild-log.local) to prevent different batch values from overwriting each other's logs. - builder add — generated hsh commands now include
--target=<arch>when the builder architecture differs from the host's native architecture (e.g., i586 builder on x86_64 host). Applies to build and rebuild commands (hsh-shellandhsh-installdo not support--target). - builder status — remote builders now correctly show "unavailable" when SSH connection fails (e.g., "No route to host") instead of incorrectly reporting them as "free".
0.18.0 - 2026-02-16¶
Added¶
- builder add — added
--repoflag for local apt config management. Generates apt.conf, sources.list, and preferences locally in~/.config/zoryn/builders.d/{name}/apt/and syncs to builder before each build. Repository release file is validated against--branch. Local paths are auto-prefixed withfile:///. - builder add — added
--apt-confand--sources-listflags to use alternative apt configuration instead of system/etc/apt/apt.conf; supportshost:pathsyntax for reading files from remote hosts via SSH. - builder add — interactive mode now prompts for repository with TAB completion (local paths via readdir, remote paths via SSH). Repository is asked before branch, and branch is auto-detected from the repository's sources.list or release file. Default value is detected from the builder host's
/etc/apt/sources.list. - builder list — now shows repository URL and local apt config status for each builder.
- task test-rebuild — show beehive FTBFS status for dependent packages; packages that already fail to build on beehive are marked with
[FTBFS since ...]in the output. Add--skip-ftbfsflag to skip these packages automatically.
Note¶
- Due to improvements in apt config storage scheme, it is recommended to re-add all builders (
zoryn builder remove <name>+zoryn builder add).
Fixed¶
- builder add — no longer silently overwrites existing apt config; in interactive mode asks for confirmation, in non-interactive mode skips with a warning.
- builder add — fixed apt-config generation failing on sources.list entries with
[label]syntax (e.g.,rpm [alt] http://...); also fixed parsing when the branch name is in the base directory field rather than the URL path. - builder add — fixed hasher satellite user detection: now correctly checks for
{username}_a{N}/{username}_b{N}users (matching actualhasher-useraddnaming convention) instead of non-existent_hasher_priv{N}_{username}. - builder add — fixed
hasher_numbervalidation:hasher-useraddrequires--number >= 1; builders without parallel builds now runhasher-useraddwithout--numberflag (creating default satellite users).
Changed¶
- spec check —
duplicate_changelog_versionsnow emits warning instead of error for old changelog entries (determined by RDB version position), matching the behavior of vulnerability and bug checks.
0.17.1 - 2026-02-15¶
Changed¶
gen environment —
parallel = "on"is now included by default in generated~/.zorynconfig. Previously it was commented out asoff. Existing configs can pick up the new default viazoryn gen environment --update-config.builder add — hasher user creation is now enabled by default. If hasher satellite users for the configured hasher number don't exist,
builder addautomatically creates them viasudo hasher-useradd. Use--no-create-hasher-usersto disable. The--create-hasher-usersflag from 0.17.0 has been removed. If hasher user creation fails (no sudo, password required), the builder is not saved — previously the config was written regardless. For bare hostnames (SSH aliases withoutuser@), the remote username is resolved via SSHwhoami. Error messages include actionable fix commands (sudoers config, manual commands).
Security¶
- http — HTTP responses are now limited to 64 MB (configurable via
Gau_http.max_response_size) to prevent memory exhaustion from oversized responses. File downloads are limited to 50 GB (configurable viaGau_http.max_download_size) to prevent disk exhaustion. Both limits useCURLOPT_MAXFILESIZELARGEfor earlyContent-Lengthrejection plus write-function enforcement for streaming/chunked transfers.head_urlis not affected by size limits since it downloads no body. - http — Added DNS rebinding SSRF protection via
CURLOPT_PREREQFUNCTION. After libcurl resolves DNS and establishes a connection, the resolved IP is validated against private/internal ranges (loopback, RFC 1918, link-local, metadata, ULA, CGNAT/RFC 6598, IPv4-mapped IPv6 in both dotted-quad and hex form). This runs before any HTTP data is sent and is checked on every redirect, closing the TOCTOU gap between hostname validation and DNS resolution. - test-rebuild — lock files are now created with
0o600permissions instead of0o644.
Docs¶
- builder — added missing
runandremovesubcommands to README documentation.
Fixed¶
- gen env — hasher workdir now uses the local system username (
$USER) instead of the gitery/gyle login. Previouslyworkdir=/tmp/.private/<gitery-login>/hasherwas written, which is incorrect since hasher runs locally. The gitery remote name now defaults to"origin"instead of being set to the SSH login. Both~/.zorynand~/.hasher/configare now always created even without--login, since they don't depend on gitery credentials. - builder add — new remote builders no longer include the old default
downloadcommand in config, which caused immediate auto-migration on firstbuilder status. Download is now handled by smart download logic.
0.17.0 - 2026-02-12¶
Fixed¶
- gen environment — added
Port 222to generated SSH config entries for gitery and gyle. Fixed SSH config documentation to usealt_usernameinstead of deprecatedgit_username(#15). - build — fixed
{hsh-rebuild}template not being expanded for remote SSH builds, causingzoryn build -rto fail with "command not found" on remote builders. - build, builder — updated help text examples from
gear-hshtohsh. - check spec —
changelog_bugscheck now uses position-based (date order) comparison instead of version comparison to determine fresh vs old entries. Fixes false errors on old changelog entries with date-based versions (e.g.20070410) in packages with epoch. - check spec — added debug output (
-dflag) for spec check diagnostics: RDB version lookup results and changelog entry classification.
Added¶
- check packages — run package quality checks (sisyphus_check, etc.) on built RPMs. Available as
zoryn check packages [PATH...]and runs automatically afterzoryn buildandzoryn up. Configurable via~/.zoryn[check.tools.*]sections. Use--skip-check=allto disable post-build checks. - builder add —
--create-hasher-usersflag to automatically create hasher satellite users viasudo hasher-useradd. - builder — validate builder name to alphanumeric characters only.
- completions — added fish shell completion for zoryn.
0.16.0 - 2026-02-08¶
Added¶
- builder add — automatic apt config generation for easy multi-branch builder setup. Apt-config files are stored per-branch in
~/hasher_N.env/{branch}/. - fuzz — coverage-guided fuzzing for 9 libraries (73 targets) using Crowbar + AFL. QuickCheck mode runs in CI per commit; AFL mode for deeper testing on schedule.
- ci — scheduled AFL fuzzing workflow (daily at 03:00 UTC) with crash artifact collection.
- builder run — run arbitrary commands inside hasher chroot (
zoryn builder run -- rpm -qa). Supports--rooterfor pseudoroot,--execute=PATHfor scripts. Works with both local and remote builders. Analogous tohsh-run. - task rebuild —
--skip LISTto skip subtasks by number or package name (e.g.,--skip 340,440,vtkaddon). Numbers match subtask IDs, strings match package names. - task rebuild —
--skip-gyle-doneto skip subtasks already built on gyle for the target architecture. Only affects--all-subtasksmode.
Changed¶
- task rebuild —
--all-subtasksmode now tracks build results in{basedir}/logs/with success/error distinction. On re-run, successfully built packages are skipped; previously failed packages are retried automatically. Use--forceto clear logs and rebuild everything (does not override--skipor--skip-gyle-done). With--force, existing package directories are re-cloned from the task. - task rebuild —
--all-subtasksmode no longer overwrites existing package directories (preserving user fixes). - builder status — combined
ensure_hasher_dirandcheck_busyinto a single SSH call for remote builders, reducing overhead from 2 SSH round-trips to 1.
Fixed¶
- version —
Version.normalizenow strips all leadingv/Vprefixes and trims whitespace after stripping (found by AFL fuzzing). - check spec —
url_reachablenow expands RPM macros (%name,%version,%define/%global) in theUrl:tag before checking reachability. Previously, URLs likehttps://example.com/%namewere checked literally. - up — fixed version update for spec files with composite macros (e.g.,
%define ver_major 1.15/%define ver_minor 3/Version: %ver_major.%ver_minor). Previously, the Version line was replaced literally, losing macro references. Now the underlying%definelines are updated correctly, preserving the macro structure. Also fixes updating through derived macros (e.g.,Version: %pkgverwhere%define pkgver %major.%minor.%patch). - up — fixed watch file parsing when comment lines have trailing backslash. Previously, a comment like
# example.org/ \would incorrectly continue to the next line, breaking parsing. Now comments are properly skipped regardless of backslash. - up — added support for watch files with regex patterns in URL directories. Watch files like lilypond's
https://example.org/v(\d+)/ pkg-(\d+).tar.gznow work correctly — zoryn first scans the base directory for matching subdirectories, then searches each for matching files (similar to Debian uscan behavior).
Changed¶
- spec —
Rpm_spec.update_versionnow returns(string, string) resultinstead of raising on error. All callers updated. - build — fixed O(n*m) performance in
read_last_lines(removed redundant inner-loop trimming). - build — close read FDs on SIGINT in parallel build mode to prevent leaks.
- build — replaced
failwithwithprint_error/exit 1for user-facing errors in remote builder configuration. - submit — renamed
-boption to-B/--branchfor target repository selection. Now supports TAB completion for branch names. - task copy — changed default behavior: task is no longer automatically started. Use
--runto start the task, and--committo auto-commit after successful build (likezoryn submit --run --commit). - task copy — automatically determines build reason (new version, bugfix, etc.) for stable branches by comparing with packages in RDB. No longer requires manual
--message.
Internal¶
- Centralized
chdir_term,debug_term,verbose_terminto sharedCli_argsmodule, removing duplication across 20+ command files. - Eliminated all uses of
Option.get,List.hd,List.tlin favor of safe pattern matching. - Changed
exittoUnix._exitin forked children to skipat_exithandlers. - Close inherited pipe FDs in forked children to prevent deadlock.
- Added SIGINT handlers for proper child cleanup in parallel builds.
- Added
EINTRretry towaitpidin parallel builds to prevent zombie children. - Escaped tag name argument in
Girar.create_gear_tagto prevent shell injection. - Escaped package names in
Sandbox.execute_hasher/execute_hybrid. - Replaced
failwithwithassert falsefor genuinely unreachable branches in libraries.
0.15.1 - 2026-02-04¶
Changed¶
- clone — no longer automatically adds gitery remote. Use
--init-giteryto create repository on gitery and add remote for pushing.
Fixed¶
- submit — now creates repository on gitery if it doesn't exist. For new packages (not yet in sisyphus), runs
init-dbinstead of trying to clone from non-existent source. - submit — use actual package Name (with macro expansion) from spec file instead of spec filename when detecting project name. Fixes "Package not found in sisyphus" error for packages where spec filename differs from Name (e.g.,
pytest-datafiles.specwithName: python3-module-pytest-datafiles). - up — show correct next step hint (
zoryn submitinstead ofzoryn build). - check spec — VCS URLs ending with
.gitare now checked viagit ls-remoteinstead of HTTP HEAD. This fixes false positives for git servers (like SourceForge) that reject HTTP requests but work correctly with the git protocol.
0.15.0 - 2026-02-03¶
Added¶
- Configurable shell commands via
[commands]section in~/.zoryn. All external commands (git, ssh, gear-*, rpm, etc.) can now be customized with paths and global arguments. {name}template substitution in shell commands with recursive expansion and subcommand support (e.g.,{git.fetch}inherits from{git}).- Validation of
[commands]config at startup: rejects unknown keys, shell metacharacters, empty values, circular references. - gen environment — interactive prompts now use readline with line editing, hints, and tab completion (consistent with
builder add).
Fixes¶
- up — always change to git root directory before running. Previously hooks from
.gear/up.d/and other tools could fail whenzoryn upwas launched from a subdirectory.
0.14.0 - 2026-01-30¶
Fixes¶
- build — use
[builders] defaultlist for multi-builder mode instead of ignoring it. Previously always fell back to architecture-based matching, selecting the implicitlocalbuilder instead of configured defaults. When bothdefaultanddefault_archare set, the default list is filtered by allowed architectures. - build — create
.gear/directory for build logs if it doesn't exist. Previously failed with "No such file or directory" when building in repositories without.gear/directory. - task batch — fix "Interrupted system call" crash when resizing terminal window during build. Now properly handles SIGWINCH by retrying
select()on EINTR.
New Features¶
- terminal title: Show current operation in terminal window title bar during long-running commands. Displays specfile name and specsubst value if set (e.g. "Zoryn: building php.spec [python3_version=3.12]", "Zoryn: batch testing: php83/php.spec [python3_version=3.12]"). Title is automatically restored on exit. Requires Konsole tab title format to include
%w. - check spec: Validate bug closure syntax in
%changelog— detects invalid formats not recognized by girar (e.g.(fix #N)instead of(Closes: #N)), bare bug references like(#NNNN), out-of-range bug numbers, non-existent bugs, and already-closed bugs via Bugzilla REST API.
0.13.1 - 2026-01-27¶
Fixes¶
- submit — reuse existing tag on HEAD instead of creating a new one
- test — fix flaky
check_host_availabletest that depended on actual system load
0.13.0 - 2026-01-27¶
Changed¶
- submit — unified task run behavior across all modes (normal,
--with,--replace). By default, tasks are created but not run (test-only mode). Use--runto start the build,--committo mark for publishing instead of test-only. Default behavior can be configured in~/.zoryn[submit]section withrunandtest_onlyoptions.
Fixes¶
- gen version-up — pass
tag_prefixandpkg_nameto tag search so tags likelibssh2-1.11.0are found correctly - gen version-up — embed tag prefix into pattern instead of emitting
strip-prefix; only emitcreate-aliaswhen tag prefix differs from.gear/rulesprefix
New Features¶
- gen version-up --filter — generate
filterconfig from version prefix (e.g.,--filter 4.5generatesfilter = "major=4, minor=5"). If.gear/version-upalready exists, adds filter to the existing config without regenerating it. - check spec — new command to validate RPM spec files before submit. Checks required fields, deprecated Packager tag, duplicate changelog versions, and URL/VCS reachability. Supports
--no-networkto skip URL checks and--spec FILEto specify spec file path. - check spec — add changelog vulnerability identifier validation — checks that CVE/BDU/OVE/MFSA identifiers are formatted so the girar parser can find them
- check spec — add
-B/--branchoption for target branch (default: sisyphus) - submit — integrated
check specvalidation before submit. Errors block submit, warnings are displayed but don't block. Use--skip-checkto bypass validation. - submit — enable spec validation network checks (URL reachability, vuln format) and pass target branch for vulnerability check
- task batch --test — local-only test build mode without gitery/gyle interaction. Build logs are saved per-package to
<log_dir>/<config>/<timestamp>/with a summary file - task batch --skip PKG1,PKG2 — skip specific packages from batch
- task batch -b NAME — select builder for local builds
Breaking Changes¶
- task batch —
-brenamed to-Bfor branch selection;-bnow selects builder
New Features¶
- --run option — explicitly run the task after creating/modifying it
- --commit option — mark task for publishing (opposite of
--test-only) - submit config — new
[submit]section in~/.zorynto configure default behavior:
0.12.0 - 2026-01-24¶
Changed¶
- submit --with now auto-replaces subtask if same tag exists
- submit --with shows prompt when package exists with different tag
New Features¶
- zoryn builder copy — new command to copy files into and out of hasher chroot environments. Supports both local and remote builders. Copy into chroot uses the
.in/staging directory withhsh-runto work around read-only chroot restrictions. Copy from chroot reads directly from the chroot filesystem. Examples: - Copy into chroot:
zoryn builder copy ./file.c mybuilder:src/ Copy from chroot:
zoryn builder copy mybuilder:build.log ./submit --replace — replaces subtask in existing task (moved from
task replace). Supports--replace=TASK_IDto specify task,--subtask=Nto select specific subtask when replacing, and--dry-runto preview actions without executing.submit --dry-run — previews all actions (git operations, girar API calls) without executing them.
--no-edit-commit option — added to
zoryn submit,zoryn task rebuild, andzoryn task batchcommands. Passes--no-edittogear-commit, allowing automated workflows without interactive commit message editing.
Removed¶
- task replace — use
submit --replaceinstead
Development¶
- bisect_ppx code coverage — added bisect_ppx as a test dependency for measuring code coverage. Run tests with coverage:
dune runtest --instrument-with bisect_ppxGenerate HTML report:bisect-ppx-report html
Internal¶
Shell.run_with_callback — new function for running commands with per-line callback, used by
cmd_build.mlfor build command execution with stage detection and logging. Supports mocking viaZORYN_TEST_FIXTURESfor integration tests.Unified hsh command mocking — all hsh/hasher commands are now mockable in tests via fixtures in
test/fixtures/hsh/. This allows integration tests to run without actually launching hasher builds.test_support library — added
zoryn.test_supportlibrary withFixture_loadermodule for parsing test fixture files. Supports YAML-like headers for specifying exit codes, stderr, and HTTP status codes in test fixtures. Includes helpers for converting URLs and SSH commands to fixture file paths.Repo_generator module — added
Test_support.Repo_generatorfor creating test git repositories from TOML configuration files. Supports specifying commits with messages, tags, and file lists, as well as git remotes. Files are copied from a template directory or created as empty files if not present. Useful for testing commands that operate on git repositories.Test_env module — added
Test_support.Test_envfor creating isolated integration test environments. Provides temporary directories, git repository generation from templates, and zoryn command execution with output capture. Includes automatic cleanup andwith_envhelper for safe resource management. Initializes HTTP and SSH mocking via ZORYN_TEST_FIXTURES.
Fixes¶
ppxlib 0.37.0 compatibility — fixed build failure with ppxlib 0.37.0 due to AST changes in ppx_shell_check.
error messages — improved error handling for common exceptions (Sys_error, Unix_error, Failure). Instead of "internal error, uncaught exception", shows clean error message like
zoryn: /path/to/file: Permission denied.zoryn task download — automatically creates destination directory if it doesn't exist.
zoryn task rebuild — fixed topological sorting with cycle handling. The RDB API returns package dependencies that can form cycles (e.g., dune ↔ ocaml-spawn). Previously, cycles caused the entire package list to be empty. Now cycles are detected and broken by selecting packages with the fewest unresolved dependencies, ensuring all packages are included in the correct build order.
0.11.0 - 2026-01-22¶
New Features¶
- zoryn task rebuild — added
--dptypeand--depthoptions for RDB dependency queries: --dptype=TYPE(source|binary|both) — specify dependency type (replaces--build-depsflag)--depth=N(1-4) — control dependency depth level These options match the behavior ofzoryn task test-rebuild.zoryn builder remove — new command to remove builder configurations. Deletes the config file from
~/.config/zoryn/builders.d/and removes the builder from the default list in~/.zoryn. The built-in 'local' builder cannot be removed. Supports Tab completion for builder names.Interactive input with linenoise in
zoryn builder add— improved interactive mode with:- Full line editing (arrow keys, Home/End, Ctrl+A/E, etc.)
- Tab completion for builder type, architecture, branch, and filesystem paths
- Default values shown as hints (gray text) — press Tab to accept
New
Gau_common.Readlinemodule for reuse in other commands[Experimental] zoryn task test-rebuild — new command for testing package rebuilds before submitting a task. Downloads RPMs from a task, identifies dependent packages via RDB, and test-rebuilds each one locally. This helps verify that packages build correctly with new dependencies before committing to a task.
Features: - Multi-architecture support with parallel execution on multiple builders - Automatic RPM download from task by architecture - Dependent package detection via RDB (with optional build-deps) - Incremental rebuild with --continue / --continue-no-refresh - Statistics saved to rebuild-stats.json (now includes task packages snapshot for comparison) - Host load monitoring to avoid overloading build machines - TUI mode with --top for interactive progress display - Build log analysis for error reporting
Example usage:
# Basic test on local builder
zoryn task test-rebuild 404809
# Test on all architectures in parallel
zoryn task test-rebuild 404809 --all-builders --arch=x86_64,aarch64
# Continue interrupted build
zoryn task test-rebuild 404809 --continue
# Interactive TUI mode
zoryn task test-rebuild 404809 --all-builders --top
Sandbox execution for git maintenance hooks — hooks in
.gear/up.dand.gear/merge-up.dare now executed in a sandboxed environment. This protects sensitive files like~/.ssh,~/.gnupg, and~/.configfrom potentially malicious scripts while allowing read-write access to the project repository.hybrid backend (default): Combines hasher chroot preparation with bwrap execution. Hasher prepares a chroot with the package's build dependencies (from spec file), then bwrap runs scripts using paths from this chroot. Provides both filesystem isolation and access to required build tools without needing setuid privileges at runtime.
- bwrap backend: Lightweight sandbox using Linux namespaces with host system paths.
- direct backend: No sandbox (legacy behavior). Use
--no-sandboxflag or configure[sandbox] runner = "direct"in config.
New CLI options for zoryn up: - --sandbox=RUNNER — choose sandbox backend (hybrid, bwrap, direct) - --no-sandbox — disable sandbox (equivalent to --sandbox=direct)
Configuration in ~/.zoryn:
[sandbox]
runner = "hybrid"
path = "/usr/bin:/bin"
[sandbox.chroot]
prepare = "gear-hsh --commit -- --mountpoints=/proc,/dev/pts,/dev/kvm --lazy-cleanup --build-srpm-only"
[sandbox.hasher]
workdir = "~/hasher"
Per-project configuration in ~/.config/zoryn/projects.d/<project>.toml:
[sandbox]
runner = "hybrid"
[sandbox.chroot]
prepare = "gear-hsh --commit -- --mountpoints=/proc,/dev/pts,/dev/kvm --lazy-cleanup --build-srpm-only"
Additional packages for chroot can be specified in .gear/version-up:
The git package is always installed. Packages are installed after chroot preparation using hsh-install.
zoryn gen environment --update-config— new option to update existing~/.zorynconfig with missing default settings. This preserves all existing values and comments while adding sections and keys that were introduced in newer zoryn versions.Smart download for remote builds —
zoryn buildnow downloads only the newly built packages from remote builders instead of the entire hasher repo directory. This significantly reduces download time and bandwidth when building on remote machines that have accumulated many packages.
The feature works by: 1. Recording the list of RPMs in hasher repo before the build 2. Comparing with the list after the build to find new packages 3. Using rsync --files-from to download only the new files 4. Falling back to parsing the build log for "Wrote:" lines if comparison fails
New builder config option: - list_rpms — command to list RPMs with mtimes (default: find {hasher_dir}/repo -name '*.rpm' -printf '%P\t%T@\n')
Old builder configs with the default download command (rsync -av {host}:{hasher_dir}/repo/ {results_download_dir}/) are automatically migrated to use smart download.
Fixes¶
Show error log path on build failure in
zoryn task rebuild --up— when a build fails in--upmode, the error log path is now displayed immediately. Previously, build output went only to the terminal with no log file saved. Now logs are saved to the same log directory as the standard rebuild mode ([rebuild] log_dirin config or/tmp/rebuild-logs/<task_id>/).Fixed invalid action error in
zoryn task rebuild --upfor updated packages — packages that were updated (not just rebuilt) were failing to be added to the task withinvalid action: gearerror because the code was using a non-existentgearaction. Now updated packages are properly committed withgear-commit, tagged withgear-create-tag, pushed to gitery, and added to the task using the standardrepoaction.Fixed builder option ignored in
zoryn task rebuild --up— the-b/--builderoption was being ignored in--upmode, always using the default local builder. Now builder resolution works correctly in--upmode, same as in other modes.Fixed
packages_to_rebuildin test-rebuild statistics —rebuild-stats.jsonnow correctly populatespackages_to_rebuildwith version and release for each dependent package. Version info is now fetched in a single RDB request instead of per-package queries. Field was renamed fromtask_packages(old stats files are still supported for backwards compatibility).Fixed shell syntax error in
--allow-unrelated-historiesmerge — git pathspecs with:(exclude)were not properly quoted, causing shell to interpret parentheses as command substitution syntax.zoryn spec version setandzoryn spec add changelog --autonow useup_template— these commands now respect the[add_changelog] up_templatesetting from~/.zorynconfig, making changelog format consistent across all zoryn commands.Fixed arrow keys and Enter in test-rebuild TUI failures view — keyboard navigation in the failures view of
zoryn task test-rebuild --topnow works correctly. Arrow keys navigate the list, Enter opens the log file inless, and Escape returns to main view. Previously, arrow keys were interpreted as ESC (because only the first byte of escape sequences was read).zoryn gen environmentnow includes[sandbox]section — the generated~/.zorynconfig now includes default sandbox settings for hook execution isolation.Fixed
zoryn task rebuild --upstopping on up-to-date packages — whenzoryn upreturns "already up to date", the command now correctly rebuilds the existing version and adds it to the task as a rebuild subtask, instead of treating it as an error and stopping.All build commands now respect
[builders] defaultconfig — previously several commands used a hardcoded local builder, ignoring the[builders] defaultsetting in~/.zoryn. Now all build-related functions use the full fallback chain: (1) configured default builder from[builders] default, (2) first available configured builder, (3) local builder. Fixed in:zoryn up,zoryn build,zoryn task batch,zoryn task rebuild, andzoryn builder config.Hasher subconfig (
hasher_number) now applies to all build commands — whenhasher_numberis configured for a builder, the--numberoption is now passed to all hasher commands (hsh,gear-hsh,hsh-rebuild,hsh-shell,hsh-install,hsh-rmchroot). This fixes parallel builds on the same machine using different hasher subconfigs. The{hasher_number}placeholder can be used in command templates and expands to 0 by default or to the configured value.
0.10.0 - 2026-01-19¶
Security Fixes¶
Use unpredictable temp file for tarball download —
zoryn up --from-tarballnow usesFilename.temp_fileinstead of a predictable path like/tmp/repo-tag.tar.gz. This prevents symlink attacks where an attacker could create a symlink at the predictable path to overwrite arbitrary files when the tarball is downloaded.Fix path traversal in download_file — filenames extracted from URLs are now sanitized using
Filename.basenameand reject.and..entries. Previously, a malicious URL ending with..could write files outside the target directory.Allowlist validation for gear-update options —
gear-update-optsfrom config is now validated against a whitelist of known flags (--all,-a,--exclude,-x,--no-compress,-n,--compress,-c,--verbose,-v,--quiet,-q). Previously, while shell metacharacters were blocked, arbitrary arguments could still be injected via spaces.Fix command injection in GPG key generation — the uid (name + email) passed to
gpg2 --quick-generate-keyis now properly escaped usingValidation.escape_shell_arg. Previously, a single quote in name/email could allow shell command injection.Enable TLS certificate verification — TLS verification is now enabled by default for HTTP requests to gyle and when downloading RPM files. This prevents MITM attacks and artifact substitution. Previously,
Curl.set_sslverifypeerwas disabled.Use HTTPS for git.altlinux.org — changed default protocol from
git://tohttps://for all git.altlinux.org operations. Thegit://protocol is unencrypted and vulnerable to MITM attacks.Use HTTPS for SRPM downloads —
zoryn task rebuildnow downloads SRPM files via HTTPS instead of HTTP, preventing potential MITM attacks during package download.SSRF protection for URL downloads — replaced all direct
curlcalls withGau_httplibrary. This adds protocol restrictions (only HTTP/HTTPS/FTP), blocks localhost and private IP ranges, and limits redirect chains. AddedGau_http.download_filefor saving files.Use system temp directory for rebuild logs —
zoryn task rebuildnow uses$TMPDIR(viaFilename.get_temp_dir_name) instead of hardcoded/tmp/rebuild-logs. This respects the system's configured temp directory and avoids predictable paths on multi-user systems.Fix shell injection in git commit messages —
zoryn upnow uses proper shell escaping for commit messages containing download URLs and SHA256 checksums. Previously, here-docs with unescaped user data could be exploited if the URL or hash contained "EOF" or special characters.Fix shell injection in builder command templates — all values substituted into builder command templates (
{packages},{tarball},{batch_repo},{host}, etc.) are now properly shell-escaped. Previously, unexpected values in these variables could allow command injection.
Improvements¶
zoryn gen version-upimprovements:- Concatenated digits support — now correctly handles tags where version digits are concatenated without separators (e.g.,
camlidl113for version1.13). Generates patterns with fixed-length captures likecamlidl{major:1}{minor:2}. - Reads
.gear/rulesfor template format — template now matches the tag format fromtar:directive in rules. For example, if rules hastar: v@version@:., template will bev{major}.{minor}instead of just{major}.{minor}. Auto-enables
create-alias— when version transformation is needed (template, strip-prefix, or strip-suffix),create-alias = trueis automatically added to the config.zoryn gen watch --gentoo-pkgPyPI support — now correctly handles Gentoo ebuilds that useinherit pypiinstead of explicitSRC_URI. For such packages (likedocutils), the standard PyPI download URL is generated automatically.migrated configuration files to TOML with legacy INI fallback and optional in-place conversion.
Cmdliner 2.x compatibility — updated CLI code to use
Cmd.makeinstead of deprecatedCmd.v. This follows the recommended cmdliner 2.x style while maintaining full backwards compatibility.Shell completions use cmdliner protocol — rewrote bash and zsh completion scripts to use cmdliner's built-in completion protocol (
--__complete). This ensures completions stay in sync with CLI definitions automatically. Options and subcommands are now completed with descriptions in zsh. Custom completions for branches, architectures, builders, and git tags are preserved.Term.Syntax for CLI definitions — refactored all CLI argument handling to use
Term.Syntaxmodule withlet+/and+binding operators instead of applicative$combinators. This improves readability and aligns with modern cmdliner patterns.Arg.Completion for option values —
--branch,--arch, and--builderoptions now use cmdliner'sArg.CompletionAPI for native shell completion. Branches are completed fromAlt_branches.active, architectures from a predefined list, and builders from configured builder names. This works automatically in bash and zsh without external scripts.zoryn builder listshows directories — now displayshasher_dirandremote_dirfor each builder in the list output.
New Features¶
zoryn task refresh— refresh stale rebuild subtasks in a task with newer versions from the repository. Compares versions of "rebuild" subtasks against current repository state and updates those that have newer versions available. Supports--dry-runto preview what would be refreshed without making changes.zoryn task replace --dry-run— preview what replace would do without making changes. Shows which task would be modified, which subtask would be deleted, and what new tag would be added.zoryn task replaceversion filter — now uses version filter from.gear/version-upto find the correct task when multiple tasks exist with the same package name but different versions. For example, withfilter = major=8, minor=5, it finds tasks with PHP 8.5.x tags instead of 8.3.x. Use-dflag for debug output showing tag filtering details.hasher_rmchrootbuilder setting — new command template for removing hasher chroot. Can be configured per-builder in~/.config/zoryn/builders.d/*.confunder[commands]section. Default:hsh-rmchroot {hasher_dir}. Supports{hasher_dir}variable.zoryn builder clean— clean hasher chroot and auxiliary directories (repo, cache, aptbox) from builders. Supports cleaning specific builders, all local (--all-local), all remote (--all-remote), or all builders (--all). Busy builders are skipped automatically. Use--dry-runto preview what would be cleaned.zoryn task batch— batch submit mode for building multiple packages from a config file. Each package is committed, tagged, built locally (optional), pushed to gitery, and added to tasks. Supports multi-branch builds with automatic task dependency setup.
Options: - <config> — batch config name (from ~/.config/zoryn/batch/) or path to .conf file - --basedir <path> — override base directory for packages - --no-build — skip local build, only commit/tag/push/submit - --dry-run — show what would be done without making changes - --create-task — create new task if not found - --refresh — refresh packages that have local changes (creates new tag, pushes, replaces subtask)
Batch config supports tag_format and tag_message options with placeholders: - {main_name} — name of the main package (e.g., "php8.4") - {main_version} — version from main package spec (e.g., "8.4.17") - {main_release} — release from main package spec (e.g., "alt1") - {name} — current package name
Default format: {main_name}-{main_version}-{main_release} (e.g., "php8.4-8.4.17-alt1")
Example batch config:
[batch]
name = PHP 8.4
specsubst = phpver=8.4
main = php8.4
# tag_format = {main_name}-{main_version}-{main_release} # default
Examples:
zoryn task batch php-8.4 # Basic batch submit
zoryn task batch php-8.4 -b sisyphus,p11,p10 # Multi-branch
zoryn task batch php-8.4 --no-build # Skip local builds
zoryn task batch php-8.4 --dry-run # Preview mode
zoryn task batch php-8.4 --create-task # Create tasks if needed
Features: - Task content caching for faster processing with large tasks - Maintains batch order when inserting packages into existing tasks - Skips packages already in task (shows task/subtask IDs) - Reuses existing tags if they point to HEAD (no unnecessary tag recreation) - Unified dry-run and normal mode (same code path, consistent behavior)
Fixes¶
zoryn task rebuild— fixed topological sort to consider build dependencies. Previously, the build order used only runtime dependencies from RDB, which caused incorrect ordering when packages have build-time dependencies on each other (e.g.,ocaml-ocamlformatBuildRequiresocaml-alcotestbut doesn't Require it at runtime). Now usesdptype=bothto get the full dependency graph including both runtime and build dependencies for accurate build ordering.zoryn up— fixed version string losing leading zeros in components. Versions like8.04.00were incorrectly simplified to8.4.0in spec files. Thefind_newer_versionsfunction now preserves the original version string from tags instead of converting throughint listwhich dropped leading zeros. Also fixedfind_all_matching_tagsto use RPM-style version comparison so that spec version8.04.00correctly matches tagv8.4.0(and vice versa).Remote builder
{remote_dir}handling — two related fixes for remote builds:- Fixed
~not expanding in{remote_dir}paths. The variable was single-quoted, preventing shell expansion of~, causing paths like~/buildto become/home/user/~/build. - For SSH-wrapped commands (
wrap_ssh=true),~is now converted to$HOMEwith double quotes so the remote shell expands it. For upload/download commands (wrap_ssh=false),~is kept as-is since rsync/scp handlehost:~/pathcorrectly. Added
ensure_remote_dirto createremote_diron remote host before upload. Previously, uploads would fail if the directory didn't exist.zoryn build— fixed exit code detection for remote builders in parallel mode. Thescriptcommand was missing the-eflag, causing it to always return 0 regardless of the actual build result. Remote builds that failed (e.g., due tohasher-privderrors) were incorrectly reported as successful ("OK" in the status table)zoryn builder add— fixed generated commands to use{hasher_dir}template variable instead of hardcoding the hasher directory value. Now the build, download, shell, and install commands will correctly use thehasher_dirsetting from the config at runtime.zoryn submit— fixed task dependency logic for multi-repo builds. Previously, tasks were chained linearly (sisyphus → p11 → c10f2), causing incorrect dependencies between unrelated branches. Now dependencies are based on build dependency chains:sisyphus → p11 → p10 → p9 → p8andsisyphus → c10f2 → c9f2. Each task depends only on predecessors in its chain (e.g., p10 depends on p11 and sisyphus; c10f2 depends only on sisyphus, not on p11)zoryn builder add— fixeddefault = ...being added before[builders]section when section didn't exist in~/.zoryn. Also now appends to existing default list instead of replacing it (e.g.default = localbecomesdefault = local, newbuilder)zoryn build --parallelandzoryn builder status— fixed race condition when multiple builders share the same host and hasher directory. Builders are now grouped by(host, hasher_dir)combination and operations run sequentially within each group, while different groups still run in parallel. Additionally, status checks are now cached by(host, hasher_dir, hasher_number)— if multiple builders share identical hasher config, only one status check is performed. Previously, builders likeshell32andshell64on the same host would interfere with each other's hasher lock checks, causing spurious "busy (PID 0)" status inbuilder statusand build failures in parallel mode.zoryn task batch— fixed task search not finding existing tasks when main package was added with batch tag format. The search was looking forpkg=version-release(e.g.,php=8.4.17-alt1), but the default batch tag format is{main_name}-{main_version}-{main_release}(e.g.,php=php-8.4.17-alt1). Now searches for both tag formats.zoryn task batch— fixed package name detection fromtask showoutput. Thepkgname=field contains SRPM name after specsubst (e.g.,php8.4-mbstring), butdir=contains the actual git repo path (e.g.,/people/.../php-mbstring.git). Added newgit_repofield toGirar.subtasktype for git repo name, now used for matching acrosstask batch,task replace,task delete,task copy, andfind_upstream_tasks.
Refactoring¶
zoryn gen watch— moved watch file utilities from inline module tolib/watch(file operations: write, find, normalize, compare, check URL) and source fetching tolib/watch_gen(Debian/Gentoo/Arch API functions); moved spec helpers (has_watch_source,add_watch_source) tolib/rpm_spec; moved gear helpers (has_watch_copy_rule,add_watch_copy_rule) tolib/gear_utils. Command now uses library functions instead of local modulezoryn clone— now usesGirar.add_gitery_remoteand remote name from~/.zorynconfig instead of hardcoded valueszoryn task rebuild --up— refactored to callCmd_up.update_packagefunction directly instead of spawning a subprocess withzoryn upshell command. The git merge workflow now has a result-based API (run_git_merge_workflow) that returnsresulttype instead of callingexit, enabling programmatic use from other commandsget_packager_from_git— moved packager detection from git config (user.name+user.email) tolib/gau_gitlibrary. TheRpm_spec.get_packagerfunction now usesGau_git.get_packager_from_gitas fallback when packager is not found in~/.rpmmacrosor viarpm --evalzoryn gen opam2spec— moved utilities from inlineSpecmodule to libraries:find_opam_files/find_opam_filetolib/opam_parser,generate_rules/rules_existstolib/gear_utils. Command now uses library functions instead of local implementationsNew
lib/opam_repolibrary — extracted opam repository API client fromcmd_gen_opam2spec.ml:get_versions,get_latest_version,fetch_opam_file,extract_dev_repo,extract_homepage,get_git_url. Provides functions for querying packages from GitHub opam-repositorylib/gau_git— addedget_latest_version_from_repoanddetect_tag_prefixfunctions for detecting version tags and prefixes from repositoryzoryn gen opam2spec— removed--buildand--verboseoptions; usezoryn buildcommand separately instead
Security¶
Validate config values for gitery/gyle —
[gitery] host,[gitery] remote, and[gyle] hostin~/.zorynare now validated to contain only valid characters (alphanumeric, dots, dashes, underscores). If invalid (e.g.gitery:/people/user/packages), a warning is shown and the default value is used. This prevents broken SSH commands and git remotes when users mistakenly put URL-like values instead of simple names.SSRF protection in HTTP client —
gau_httpnow includes comprehensive SSRF protection:- Restricted protocols to HTTP, HTTPS, FTP, and FTPS only (blocks
file://,gopher://, etc.) - Blocked access to internal/private IP ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16) and localhost variants
- Blocked cloud metadata endpoints (169.254.169.254, metadata.google.internal)
Limited redirect chain to 10 hops maximum
Fixed shell injection vulnerabilities — added proper shell argument escaping using
Gau_common.Validation.escape_shell_argthroughout the codebase. Previously, external data like git tags, version strings, package names, and file paths were interpolated directly into shell commands, potentially allowing command injection. Now all external data passed to shell commands is properly escaped using single-quote escaping.
Affected modules: - gau_git.ml — git operations (merge, fetch, tag creation, etc.) - girar.ml — gitery/gyle SSH commands (task add, push, etc.) - cmd_task.ml — rebuild and batch operations
Note: Git tags can contain dangerous characters like $, `, ;, |, & which could be used for command injection (e.g., v1.0$(id) or v1.0`whoami`). The fix ensures these are properly escaped before being passed to shell commands.
- Added ppx_shell_check — compile-time static analyzer for detecting potential shell injection patterns. The PPX checks all
Shell.run*calls withPrintf.sprintfand reports errors if arguments aren't wrapped inescape_shell_argorFilename.quote. This prevents future regressions by catching unsafe patterns during compilation.
Usage: - Automatic: enabled for all libraries and bin/zoryn via (preprocess (pps ppx_shell_check)) - Explicit: [%shell Printf.sprintf "git tag %s" (escape tag)]
New dependency: ocaml-ppxlib-devel
Path traversal protection in batch config —
basedirand packagepathvalues in batch config files are now validated to prevent path traversal attacks using..sequences. Config files with traversal sequences (e.g.,basedir = /home/../etc) are rejected with an error. AddedValidation.has_path_traversalandvalidate_path_no_traversalfunctions for reuse. Theppx_shell_checkwas also enhanced to detectFilename.concatwith..literals.Validate SSH host in builder config — the
hostvalue in builder configuration files (~/.config/zoryn/builders.d/*.conf) is now validated usingis_valid_ssh_host. Valid formats:hostoruser@host. Colons are rejected becausehost:portsyntax doesn't work with ssh command directly — use SSH config (~/.ssh/config) for non-standard ports instead. Invalid hosts are rejected with a warning.Escape specs_dir in builder commands — the
specs_dirpath (derived fromhasher_dir) is now properly escaped in shell commands withinget_builder_spec, preventing potential command injection ifhasher_dircontains special characters.URL-encode parameters in RDB API calls — package names and branch names are now properly URL-encoded before being inserted into API URLs. This prevents issues with packages containing special characters like
+(e.g.,g++),&,=, or spaces.Validate package names in opam2spec and clone — package names are now validated using
Validation.validate_package_namebefore being used in file paths, shell commands, or spec generation. Valid characters: alphanumeric, dashes, underscores, dots, and plus signs. Names cannot start with a dash. This prevents command injection via malicious opam package names (e.g.,foo$(rm -rf /)) and path traversal attacks (e.g.,../../../etc/passwd).
Affected commands: - zoryn gen opam2spec — validates RPM package name before generating spec file - zoryn clone — validates package name before cloning - zoryn task batch --up — validates package names when preparing directories
0.9.0 (2026-01-15)¶
Breaking Changes¶
[build] commandconfiguration — the build command in~/.zorynshould now be anhshcommand instead ofgear-hsh. The unified tarball workflow creates tarball separately withgear --commit, then passes it to hsh. If you havegear-hshconfigured, you'll see a warning.
Migration: Change your ~/.zoryn from:
[build]
command = gear-hsh --commit --verbose -- -v --mountpoint=/proc,/dev/pts,/dev/kvm --lazy-cleanup
The default command (when not configured) changed from gear-hsh to hsh --lazy-cleanup.
New Features¶
- Interactive TUI mode (
--top) — new htop-like interface for monitoring multi-builder builds in real-time. Usezoryn build --all-builders --topto see a live table with builder status, current stage, and log streaming. Currently available forbuildcommand. Features: - Real-time builder status table with colored indicators
- Live log panel with scrolling (Up/Down, PgUp/PgDn, Home/End)
- Mouse wheel support for log scrolling
- Regex search in logs (
/to search,n/Nfor next/prev match, search history) - TAB to switch between builders
- Cancel (
c) and restart (r) builds interactively - Proper signal forwarding to remote builders on cancel (uses
ssh -tt) - Results overlay when builds complete
- Header shows full project path, version in bottom right corner
- Colored UI elements (header, keys, selected builder, scroll position)
Requires lambda-term library (
ocaml-lambda-term-devel)Skip cleanup in debug mode — when using
-dflag withzoryn build, the tarball files (both local and remote) are not deleted after build. This helps with debugging failed builds by preserving the tarball for inspection.Parallel builder status checks —
zoryn builder statusnow checks all builders in parallel using fork (one process per builder). This significantly speeds up status checks when you have multiple remote builders, as SSH connections are made concurrently. Status output now shows the spec file in builder's hasher chroot (e.g.busy (PID 12345) [dune.spec]).Improved parallel build visualization — parallel builds (
zoryn build --parallel) now show a table with columns for each builder. A new row is printed whenever any builder's stage changes:- Time column shows elapsed time since build start
- Each builder column shows current stage (%prep, %build, %check, etc.) or final status (OK/FAIL/SKIP)
Failed builders are highlighted and their last 8 log lines are shown at the end
Per-project changelog template —
[add_changelog]section in.gear/version-upnow takes priority over global~/.zorynconfig. Useup_templateto customize changelog entry format per project. Variables:{old_version},{new_version},{cves}.zoryn builder config [name]— new command to show full builder configuration including all automatic and default values. Without arguments shows default local builder. Useful for debugging builder setup and understanding what commands will be executed.zoryn builder list --simple— new option to output only builder names, one per line. Useful for shell scripts and completion.Comma-separated
--builderoption —--buildernow accepts multiple builder names separated by commas (e.g.,--builder=shell32,shell64) inzoryn build,zoryn up, andzoryn task rebuildcommands.Default builder configuration — new
defaultoption in~/.zoryn[builders]section. When set, this builder is used byzoryn build,zoryn up, andzoryn task rebuildcommands when no--builder/--arch/--branchoptions are specified. Example:default = local.Default architecture configuration — new
default_archoption in~/.zoryn[builders]section. When set, selects builders by architecture. Comma-separated list for multiple architectures (e.g.,default_arch = x86_64, aarch64). Multi-builder mode is enabled automatically when multiple architectures are specified.Parallel mode configuration — new
paralleloption in~/.zoryn[builders]section. Set toonoroffto control default parallel execution in multi-builder mode (default:off). Can be overridden with--parallelor--sequentialflags.--sequentialflag — new-s, --sequentialflag inzoryn build,zoryn up, andzoryn task rebuildcommands. Forces sequential build execution, overridingparallel = onin configuration. Useful when builds have interdependencies.zoryn task rebuild --all-subtasks— new option to rebuild all packages from a task locally (not just dependencies). For each subtask: clones package to BASEDIR/package, checks out the tag from task, builds locally with hasher. Supports both gear (git) and srpm subtasks.
Internal¶
- Unified build API in Builder module — new functions for building packages:
Builder.create_gear_tarball— create tarball from gear repositoryBuilder.build_file— build a file (tarball or src.rpm) on a single builderBuilder.build_file_multi— build on multiple builders (parallel or sequential)Builder.build_result— type for build operation results
Unified workflow for both local and remote builders: always use tarball (gear --commit → tarball → hsh). This simplifies the codebase and ensures consistent behavior across all builder types.
All build commands (zoryn build, zoryn up, zoryn task rebuild) now use the unified Builder API, eliminating duplicate build logic across modules.
Fixes¶
TUI cancel kills all builders instead of selected one — fixed issue in
zoryn build --topwhere cancelling one builder (ckey) would terminate all running builds. The problem was that child processes inherited SIGTERM handler from parent, which calledcleanup()to kill all builders. Now signal handlers are reset to default in child processes immediately after fork, before any other code runs.Remote build output garbled — fixed shifted/garbled output during remote builds. The issue was caused by
ssh -tt(PTY allocation for signal forwarding) which changes local terminal settings. Now SSH commands with-ttare wrapped withscriptto create a separate PTY, isolating SSH from the main terminal while preserving signal forwarding to remote builders.zoryn task replacesubtask position not preserved — fixed replace to insert the new subtask at the same position where the old one was. Previously, the new subtask was always added at the end of the task, breaking build order dependencies.--builderoption ignored whendefault_archhas multiple values — fixed builder resolution logic. When--builderis explicitly specified, it now takes priority overdefault_archconfiguration. Previously,--builder shell32could result in using all builders fromdefault_archinstead of just the specified one.Builder selection ignores busy status — when selecting builder by
--archor--branch, now prefers free builders over busy ones. Previously, if the first matching builder was busy, zoryn would fail even when other matching builders were available.zoryn task rebuild --all-subtasks— fixed gear builds on remote builders. Previously, remote builders with{tarball_name}in build command template failed because tarball was not created and uploaded. Now uses proper tarball workflow: creates tarball locally withgear --commit, uploads to remote, then runs hsh.Builder
{tarball_name}expansion — fixed variable expansion for local builders in file-based builds (srpm). Now{tarball_name}and{tarball}are properly expanded in build command templates for local builders too.zoryn gen opam2spec— fixed main package detection for multi-package projects. Now correctly reads project name from(name ...)in dune-project file. Previously failed on projects likecohttpwhere opam files include unrelated packages (e.g.,http).zoryn gen opam2spec --clone— fixed version selection and tag handling.- Correct opam version sorting (v0.17.0 is newer than v0.12.0)
- Clone only the needed tag using
--branch --single-branch - Detect tag prefix from found tag and generate correct
.gear/rulesformat Support legacy
opamfile without.opamextensionzoryn upparallel build log spam — fixed parallel builds inzoryn upoutputting build logs to screen. Now parallel builds show only the progress table (same behavior aszoryn build --parallel). Added-v, --verboseflag to control build output (default: off, same aszoryn build).
Internal¶
- opam_parser — replaced regex-based dune-project parsing with proper S-expression parsing using
parsexplibrary. Added tests with cohttp-style dune-project fixture.
0.8.0 (2026-01-13)¶
New Features¶
- New
zoryn buildercommand — manage build machines (builders) for local and remote builds. Builders are configured in~/.config/zoryn/builders.d/*.conffiles. Each builder can specify architecture, branch, and custom commands for building, shell access, and package installation. zoryn builder list— list all configured builderszoryn builder status— check if builders are free, busy, or unavailablezoryn builder shell [--builder=NAME]— open shell in hasher (wrapper over hsh-shell)zoryn builder install [--builder=NAME] PKG...— install packages in hasher (wrapper over hsh-install)zoryn builder add [options]— create new builder configuration. Works in interactive mode by default (asks questions), or non-interactively when--nameis specified. Options:--name,--type(local/remote, default: remote),--host,--arch(auto-detected from host),--branch,--hasher-dir,--remote-dir,--description,--hasher-number,--based(copy from existing),-y(auto-confirm). Example:zoryn builder add --name arm --host myserver -y
Builder configuration supports variables: {host}, {remote_dir}, {hasher_dir}, {tarball}, {tarball_name}, {results_download_dir}, {git_root}, {packages}, {batch_repo}, {arch}, {name}. The check_busy command checks if hasher is currently running by reading the pid file.
- Batch build RPM synchronization — new commands for syncing RPMs between builders during batch builds:
download_rpms— download RPMs from builder after each build (for accumulating in batch_repo)upload_rpms— upload accumulated RPMs to builder before each build (so dependencies are available)- Configure
batch_repoin~/.zoryn [builders]section to set the local directory for RPM accumulation Default commands are auto-generated:
cpfor local builders,rsyncfor remote buildersBuilder support in
zoryn build— select which builder to use for building:-b, --builder=NAME— use specific builder by name-a, --arch=ARCH— select builder by target architecture. Multiple architectures can be specified comma-separated (e.g.,x86_64,aarch64)-B, --branch=BRANCH— select builder by target branch. Multiple branches can be specified comma-separated (e.g.,sisyphus,p11)-w, --wait— wait for busy builder to become free--skip-busy— skip busy builders without error--all-builders— build on all matching builders (filtered by--arch/--branchif specified)-p, --parallel— run builds in parallel when using--all-builders(default is sequential)
Examples: - zoryn build --builder=arm-server — build with specific builder - zoryn build --arch=aarch64 — build for ARM64 architecture - zoryn build --all-builders --arch=aarch64,x86_64 — build for multiple architectures - zoryn build --branch=p11 --wait — build for p11, waiting if builder is busy - zoryn build --all-builders — build on all configured builders - zoryn build --all-builders --arch=x86_64 --parallel — build on all x86_64 builders in parallel
- Builder support in
zoryn up— select which builder to use for building after version update: -b, --builder=NAME— use specific builder by name-a, --arch=ARCH— select builder by target architecture. Multiple architectures can be specified comma-separated (e.g.,x86_64,aarch64)-B, --branch=BRANCH— select builder by target branch. Multiple branches can be specified comma-separated (e.g.,sisyphus,p11)--all-builders— build on all configured builders-p, --parallel— run builds in parallel when using--all-builders-w, --wait— wait for busy builder to become free--skip-busy— skip busy builders
Examples: - zoryn up --builder=arm-server — update and build with specific builder - zoryn up --arch=aarch64 — update and build for ARM64 architecture - zoryn up --all-builders --arch=aarch64,x86_64 — update and build for multiple architectures - zoryn up --all-builders --parallel — update and build on all builders in parallel
- Builder support in
zoryn task rebuild— select which builder to use for rebuilding packages: --builder=NAME— use specific builder by name-a, --arch=ARCH— select builder by target architecture. Multiple architectures can be specified comma-separated (e.g.,x86_64,aarch64)-B, --build-branch=BRANCH— select builder by target branch (note:-bis reserved for task's target repository)--all-builders— build on all configured builders-p, --parallel— run builds in parallel when using multiple builders-w, --wait— wait for busy builder to become free--skip-busy— skip busy builders
For remote builders, uses configured upload, build, and cleanup commands from builder config. The {tarball} variable is substituted with the src.rpm path. Examples: - zoryn task rebuild 404809 --builder=arm-server — rebuild on specific builder - zoryn task rebuild 404809 --arch=aarch64 — rebuild for ARM64 architecture - zoryn task rebuild 404809 --all-builders --parallel — rebuild on all builders in parallel
- New
zoryn gen version-upcommand — generate.gear/version-upconfiguration file from current spec version. Auto-detects git tag for the version and creates pattern/template configuration for version conversion. This helpszoryn upfilter tags correctly, e.g., to avoid confusing semver tags (0.9.2) with date-based tags (20220125). -t, --tag=TAG— specify tag explicitly if auto-detection fails--to-version=VERSION— specify resulting version (use with --tag when formats differ)-f, --force— overwrite existing.gear/version-upfile- Automatically detects and handles:
- Tag prefixes (e.g.,
v,release-,FIREFOX_) - Tag suffixes (e.g.,
_RELEASE,_stable) - Different separators in tag vs version (
.vs_)
- Tag prefixes (e.g.,
Examples: - zoryn gen version-up — auto-detect tag and generate config - zoryn gen version-up --tag v1.2.3 — specify tag explicitly - zoryn gen version-up --tag osec-12_11_22.11 --to-version 12.11.22.11 — complex mapping - zoryn gen version-up --tag FIREFOX_65_0_2_RELEASE --to-version 65.0.2 — prefix + suffix - zoryn gen version-up --force — overwrite existing config
Fixes¶
task rebuild: use RDB API instead of gyle SSH —
zoryn task rebuildnow uses RDB API (Rdb.get_task_info) to get task information instead of SSH to gyle. This works without direct access to gyle server and is more reliable.version_mapper: pattern-only config support — fixed
.gear/version-upparsing to work when onlypatternis specified withouttemplate. Now the template is auto-generated from pattern by removing:+markers. This allowszoryn gen version-upgenerated configs to work correctly withzoryn up.version_mapper: tag filtering for pattern-only configs — fixed tag filtering to correctly include tags that match the pattern even when the converted version equals the original tag. This is important for pattern-only configs where the goal is to filter out non-matching tags (like date-based tags
20220125) rather than transform them.
0.7.5 (2026-01-13)¶
New Features¶
- New
zoryn buildcommand — run local hasher builds with batch support. Reads build command from~/.zorynconfig ([build] command, default:gear-hsh). If the package has batch configuration with specsubst values, builds are run for each value automatically. -v, --verbose— show full build output on screen (default: silent with stage tracking)-l, --log-dir— save build logs to specified directory instead of.gear/In silent mode, build stages are displayed with timing: %prep, %build, %install, etc.
task rebuild --up — new mode for updating packages from upstream instead of rebuilding. For each dependent package: clones if needed, fetches gears/sisyphus, runs
zoryn up. If already up-to-date, adds as rebuild subtask. If updated, builds locally and adds as regular subtask. Stops on any error to allow maintainer intervention.
Example: zoryn task rebuild 404809 --up ~/packages
- task rebuild --build-deps — include build-time dependencies when querying RDB. By default only binary (runtime) dependencies are returned. With this flag, packages that build-depend on the target are also included. Useful when a package update affects packages that use it only at build time (e.g., ocaml-odoc that requires ocaml-bos-devel for building but doesn't depend on it at runtime).
Example: zoryn task rebuild 404809 --build-deps
task rebuild --up: auto-add gears remote — when using
--upmode, if thegearsremote doesn't exist in a package directory, it is automatically added before fetching. This allows working with packages that were cloned from other sources or have only theoriginremote configured.task rebuild --up: always build locally — packages are now always built locally with hasher, even when
zoryn upshows "already up-to-date". This ensures rebuilt packages are available in hasher's repo for subsequent builds that depend on them.New
zoryn gen environmentcommand — initialize ALT Linux development environment configuration files:~/.zoryn— zoryn config with [build], [clone], [sources] sections~/.ssh/config— SSH aliases for gitery and gyle~/.hasher/config— hasher workdir configuration~/.rpmmacros— %packager macro~/.gitconfig— git user.name and user.email (if not set)
Asks for name, email and gitery/gyle login with smart defaults from git config. Checks for missing packages (gear, hasher, rpm-build) and prompts to install.
0.7.4 (2026-01-12)¶
Breaking Changes¶
- Renamed
zoryn buildtozoryn submit— the command that commits, tags, pushes and submits packages for build has been renamed frombuildtosubmit. This better reflects the command's purpose: submitting a task to the gyle build system. The oldzoryn buildcommand no longer exists. Update your scripts and aliases accordingly.
0.7.3 (2026-01-12)¶
New Features¶
- up: gear-update options in config — added new
[tarball]section to.gear/version-upconfiguration file. Thegear-update-optsoption allows specifying extra options forgear-updatecommand, useful for packages where the upstream archive has incorrect directory structure. For example, addinggear-update-opts = --allwill extract all directories from the archive instead of selecting one.
Example configuration:
- gen watch: multi-source fallback (Gentoo, Arch) — when Debian watch file is not found,
zoryn gen watchnow automatically queries Gentoo ebuild and Arch PKGBUILD as fallback sources. This enables watch file generation for packages that exist in Gentoo or Arch but not in Debian.
Sources are queried via Repology API, then ebuild/PKGBUILD is fetched and parsed to extract download URL patterns. Bash variable substitutions (${PV}, ${PV/.}, ${pkgver//.}, etc.) are converted to Debian watch regex patterns with appropriate uversionmangle rules.
When multiple sources are found, a comparison table is displayed showing versions and URL patterns from each source. If patterns match across sources, the best source is automatically selected (Debian > Gentoo > Arch, newer version preferred).
New options: - --gentoo-pkg <name> — specify Gentoo package name explicitly - --arch-pkg <name> — specify Arch package name explicitly - --no-compare — skip comparison table, use first found source
Package lookup uses native distribution APIs for reliable name matching: - Arch: https://archlinux.org/packages/search/json/?name=<pkg> (exact match) - Gentoo: https://packages.gentoo.org/packages/<category>/<name>.json with automatic category search across common categories (media-sound, dev-libs, etc.) - Fallback: Repology API for packages with different project names
URL accessibility warning: After generating a watch file, the command checks if the URL is accessible and contains a directory listing. If the URL returns an error (403, 404) or has no links, a warning is displayed explaining that zoryn up may not be able to find new versions automatically. This helps identify sites like monkeysaudio.com that don't provide directory listings.
Example output:
Sources found:
Debian: not found
Gentoo: media-sound/mac 11.91
Arch: mac 11.88
Sources comparison:
+--------+---------+------------------------------------------------+
| Source | Version | URL pattern |
+--------+---------+------------------------------------------------+
| Gentoo | 11.91 | https://monkeysaudio.com/files/MAC_*_SDK.zip |
| Arch | 11.88 | https://monkeysaudio.com/files/MAC_*_SDK.zip |
+--------+---------+------------------------------------------------+
URL patterns match across sources.
Using Gentoo (newer version).
task download: accept task ID —
zoryn task downloadnow accepts both package names and task IDs. If the argument is a number, it is used directly as task ID, skipping the package search step. Example:zoryn task download 404784task download: custom output directory — new
-o/--outputoption allows specifying a custom destination directory for downloaded RPMs. The directory will be created if it doesn't exist. When using-o, hasher environment is not required. Example:zoryn task download 404784 -o /tmp/rpmsbuild: batch specsubst support —
zoryn buildnow supports packages with batch builds configured via[batch]section in.gear/version-up. For packages withspecsubstvariable and multiple values (like dune with bootstrap, ordering, etc.), the command automatically creates separate tags for each subpackage value.
Each tag is created using gear-create-tag with -n for custom name, -m for message, and -s for specsubst variable. All subtasks are added to a single build task.
Configuration options: - specsubst — variable name for gear-create-tag -s option (required) - values — comma-separated list of values in build order (required) - tag-name — tag name format (default: {base}-{value}) - tag-message — tag message format (default: {name} {value} {base})
Format placeholders: - {base} — base tag name (e.g., 3.20.2-alt1) - {value} — current specsubst value (e.g., bootstrap) - {name} — package name (e.g., dune)
Example .gear/version-up:
[batch]
specsubst = subpackage
values = bootstrap, ordering, dyn, stdune, private-libs
tag-name = {base}-{value}
tag-message = {name} {value} {base}
When running zoryn build, the output shows:
Step 3: Creating gear tag(s)...
Base tag: 3.20.2-alt1
Creating tags for 5 subpackages (specsubst=subpackage)...
Created: 3.20.2-alt1-bootstrap
Created: 3.20.2-alt1-ordering
...
Note: Batch builds with multiple repositories (-b sisyphus,p11) are not supported due to complexity.
- task rebuild: implemented —
zoryn task rebuildcommand is now fully functional. It queries RDB API for packages depending on a task, locally rebuilds each using hasher, and adds successfully built packages to the same task as rebuild subtasks.
The workflow: 1. Query RDB API for dependent packages (or parse from FAILED log with --from-log) 2. Find src.rpm for each dependent package in srpms_path 3. Run hasher build locally 4. If build succeeds, add rebuild subtask to the task
The --from-log option searches through all available logs (across iterations and tries) and finds the last one containing "ACLs of affected packages" section. This handles cases where a task has multiple iterations, some successful and some failed — it will find the relevant FAILED log even if a newer successful log exists.
For old or deleted tasks (not found in Girar), --from-log mode displays the list of affected packages without attempting to rebuild, since there's no task to add rebuilds to.
Build logs are saved to log_dir (default: /tmp/rebuild-logs/<task_id>/). Failed builds are saved as error-<pkg>.log and skipped on subsequent runs unless --force is used.
Configuration in ~/.zoryn:
[sources]
srpms_path = /path/to/srpms
[rebuild]
command = hsh -v --lazy-cleanup
log_dir = /tmp/rebuild-logs
Options: - -b <repo> — target repository (for searching task by package name) - --deps-by-pkg <pkg> — query dependencies by package name - --from-log — parse affected packages from FAILED log instead of RDB - -f/--force — retry failed packages, rebuild even if already in task
Examples:
zoryn task rebuild 404809 --from-log # rebuild from FAILED log
zoryn task rebuild 404809 # rebuild deps of TESTED task
zoryn task rebuild 404809 --force # retry failed packages
zoryn task rebuild 404809 --deps-by-pkg ocaml-foo
Note: For FAILED tasks, use --from-log to get the exact list of affected packages from the task log. For TESTED/EPERM tasks, RDB API is used.
- task copy: new command — copy all subtasks from an existing task to a new task in another repository. The new task will depend on the source task (unless it's DONE). Useful for copying sisyphus tasks to stable branches after success.
Example: zoryn task copy -b p11 404765
Fixes¶
up: apply uversionmangle in tarball workflow — the
uversionmangleoption in watch files is now applied when extracting versions. This allows converting upstream version formats like1191to11.91using rules such asuversionmangle=s/(\d{2})(\d{2})/$1.$2/up: apply pagemangle and downloadurlmangle — watch file options
pagemangleanddownloadurlmangleare now applied during version fetching.pagemangletransforms HTML before extracting links, useful for pages without direct download URLs (e.g., mumps-solver.org).downloadurlmangletransforms extracted URLs.packaging: fix bash-completion install path — bash completions now install to
/usr/share/bash-completion/completions/zoryninstead of incorrect/usr/share/zoryn/bash-completion/completions/zoryn.
Packaging¶
- man pages — added man pages for all 25 commands and subcommands, generated from cmdliner
--help=groffoutput during build.
0.7.2 (2026-01-10)¶
New Features¶
- up: support for unrelated history merge — new
--allow-unrelated-historiesflag forzoryn upenables merging upstream changes when the local branch has no common ancestor with upstream (e.g., packages with imported pristine upstream sources).
The workflow: 1. Merges with -s ours --allow-unrelated-histories to preserve local history 2. Restores upstream files except ALT-specific directories 3. Commits the merge preserving local customizations
Preserved paths are determined from .gear/rules: - .gear/ directory (always) - Directory containing spec file (spec: directive) - Directories from copy: directives (e.g., altlinux/)
Example for a package with .gear/rules:
Usage: zoryn up --allow-unrelated-histories --tag v2.0.0
- up: force update from prerelease to stable — new
--force(-f) flag enables updating when no newer version exists but current tag is a prerelease and a stable release is available. Useful for packages tracking beta/rc versions.
Without --force, zoryn will suggest using the flag when a stable release is available:
No newer versions found.
However, current tag '15.0-beta2' is prerelease and stable '15.0' is available.
Use 'zoryn up --force' to update to stable release.
Usage: zoryn up --force
up: warning for non-executable hooks —
zoryn upnow displays an error when hook directories (.gear/up.d/,.gear/merge-up.d/) contain non-executable files, suggestingchmod +x. Editor backup files (.swp,~,.bak,.orig,.rej) are automatically ignored.build: multi-repository support —
zoryn build -bnow accepts comma-separated repository list (e.g.,sisyphus,p11) and builds with dependency chaining. Repositories are sorted by level (fresher first), and each subsequent build depends on the previous task. Bash completion updated to support comma-separated values.
Usage: zoryn build -b sisyphus,p11
Note: --with option is not supported with multiple repositories.
task replace: auto-commit —
zoryn task replacenow automatically commits uncommitted changes usinggear-commit -abefore creating the tag, matching the behavior ofzoryn build.task copy: copy task to another repository — new command
zoryn task copycopies all subtasks from an existing task to a new task in another repository. The new task automatically depends on the source task.
Usage: zoryn task copy -b p11 404765
Useful for copying sisyphus tasks to stable branches after they complete.
Fixes¶
- up: use version mapper for explicit --tag — when using
zoryn up --tag, the version is now extracted using the version mapper config if available. This fixes version extraction for packages with non-standard tag formats likerelease-2.4.07.
Example .gear/version-up:
Now zoryn up --tag release-2.4.07 correctly extracts version 2.4.07. Version comparison already handles leading zeros numerically (07 > 3).
- watch: fix URL resolution for absolute paths — fixed href resolution when links start with
/(common on GitHub tags pages). Now correctly extracts domain from base URL instead of appending to full path.
Before: https://github.com/user/repo/tags//user/repo/archive/... (broken) After: https://github.com/user/repo/archive/... (correct)
up: fix tarball scheme detection for packages with options in tar directive — fixed
tar_line_uses_version_templateto only check the tag/directory part, ignoring options likename=@name@-@version@. This fixes scheme detection for packages like doxygen withtar.gz: dirname name=@name@-@version@.src.up: add .gear/autobuild.watch support — added
.gear/autobuild.watchto the list of standard watch file locations (priority 3, after.gear/watch).
0.7.1 (2026-01-09)¶
New Features¶
- up: remote changelog support for CVE detection —
zoryn upcan now fetch security advisories from URLs instead of local files. Configure in.gear/version-up: Supported parsers: osv-json/json— OSV JSON format (used by curl, etc.)markdown/md— standard markdown changeloghtml/html-table— HTML table with CVE infoauto— auto-detect based on content (default)
The remote URL is fetched, parsed for CVE fixes in the version range, and CVEs are included in the RPM changelog entry.
spec add changelog: remote changelog support —
zoryn spec add changelog --autonow also supports fetching CVE information from remote URLs (same configuration aszoryn up).changelog: extended CVE format with descriptions — new
Supported formats:cve_formatoption for changelog entries. Configure in.gear/version-up:compact(default) — inline format:(Fixes: CVE-..., CVE-...)extended— multiline with descriptions:
Fixes¶
- changelog: wrap long CVE lists at 80 characters — CVE lists in changelog entries are now wrapped with 2-space continuation indent to keep lines under 80 characters.
0.7.0 (2026-01-02)¶
Breaking Changes¶
- gen spec renamed to gen opam2spec — command renamed to prepare for other spec generation formats (Java, Python). Use
zoryn gen opam2specinstead ofzoryn gen spec.
New Features¶
clone: new command to clone packages from sisyphus —
zoryn clone <package>automatically detects whether a package was built as gear or srpm and clones from the appropriate public repository (git://git.altlinux.org/gears/...or.../srpms/...). Sets 'gears' as remote name and adds 'gitery' remote for pushing.all commands: add -V option for version display — all zoryn commands now support
-Vflag to show the current zoryn version.up: add --tag option for explicit tag selection — when the current version tag cannot be automatically detected (e.g., previous maintainer made a non-standard tag like
1.4.0.19.3bbd), you can now specify the exact upstream tag to merge:zoryn up --tag v1.5.0. This skips automatic version detection and uses the specified tag directly for merge.up, build: colored terminal output — headers, steps, errors, warnings and success messages now use colors for better visibility. Errors are red, success is green, warnings are yellow, headers are cyan. Colors are automatically disabled when output is not a TTY.
up, check version: git URL fallback when no watch file — for tarball-based packages without a watch file, zoryn now checks VCS tag (primary) and URL field (fallback) in the spec. If it's a GitHub or GitLab URL, it fetches tags via
git ls-remoteand compares versions. Bothzoryn upandzoryn check versionnow use the same unified implementation fromUpstream_detectlibrary.up: auto-download tarball from GitHub/GitLab — when updating via git URL fallback (no watch file),
zoryn upnow automatically downloads the tarball from GitHub or GitLab, runsgear-update, and completes the full update workflow. The tarball URL is constructed from the tag name (GitHub uses/archive/refs/tags/{tag}.tar.gz, GitLab uses/-/archive/{tag}/). SHA256 checksum is calculated and included in the commit message.gen opam2spec: add --get-version option — allows specifying a specific opam package version instead of using the latest. The corresponding git tag is checked out. Example:
zoryn gen opam2spec --clone cmdliner --get-version 1.2.0
Fixes¶
gen opam2spec: add %package devel for main package — multi-package specs now correctly define
%package develsection for the main package, fixing the "Package does not exist" error for%files devel.gen opam2spec: fix duplicate main package in multi-package specs — when generating spec for multi-package projects (like atd), the main package was duplicated as both the main package and a subpackage. Now correctly generates main package once and only creates subpackages for other opam files.
gen opam2spec: rename directory to match main package — when using
--clone atdgenbut the main package isatd, the directory is now renamed fromocaml-atdgentoocaml-atdto match the actual package name.gen opam2spec: fix menhir and python dependencies — menhir is a tool, not a library, so it now maps to
ocaml-menhirinstead ofocaml-menhir-devel. Also added mapping forconf-python-3andconf-python-3-develtopython3-dev.up: improve scheme detection for tar directories — detection now analyzes recent commits in the tar directory from
.gear/rules(e.g.,tar: fox). If recent commits are merge commits → git workflow. If recent commits are regular commits → tarball workflow. This correctly handles packages that switched from git-merge to tarball workflow (like libfox). Usesgit log -mto include merge commits in path-filtered output.watch: fix combined URL/pattern and multiple capture groups — watch files with combined URL and pattern like
http://example.org/ftp/pkg-(1)\.(6)\.(\d+)\.tar\.gzare now correctly split into base URL and pattern. Also fixed version extraction when pattern uses multiple capture groups — all groups are now joined with dots (e.g.,(1)\.(6)\.(\d+)extracts1.6.58instead of just1).build: fix vim input in gear-commit — interactive commands like
gear-commitandgear-create-tagnow correctly receive terminal input. Previously, vim editor launched by gear-commit had non-functional cursor input becauseUnix.open_process_indisconnected stdin from the terminal. Now usesUnix.create_processwith full terminal access for these commands.spec: handle unexpanded macros in package name — when a spec file uses specsubst placeholders like
@subpackage@, the package name may contain unexpanded%subpackagenamemacro (e.g.,dune%nil). Now correctly extracts the base name by truncating at%character.up: auto-resolve subtree merge conflicts — when
git subtree pullencounters conflicts (common when upstream has significant restructuring), zoryn now automatically resolves all conflicts in favor of upstream and completes the merge. This is the expected behavior for subtree workflows where local modifications to upstream code are typically not made.up: fix tag matching for packages with digits in name — packages like
libssh2where the name contains digits now correctly find upstream tags. The tag format is now derived from.gear/rules(e.g.,tar: @name@-@version@:.→ prefixlibssh2-), ensuring tags likelibssh2-1.11.0are properly matched instead of incorrectly stopping at the digit and treatinglibsshas the prefix.gen opam2spec: fix dependency name mapping — opam package names with underscores are now correctly converted to hyphens for ALT RPM names (e.g.,
alt_releases_matrix→ocaml-alt-releases-matrix-devel)- gen opam2spec: fix RDB dependency check — now correctly checks binary packages instead of source packages when verifying build dependencies exist in Sisyphus
- gen opam2spec: add ocurl mapping —
ocurlopam package correctly maps toocaml-curl-develALT package - gen opam2spec: use only first paragraph for description — RPM spec description is now concise instead of including full opam documentation
New Features¶
gen watch: --debian-pkg option and Repology lookup — when ALT and Debian package names differ (e.g.,
libfoxin ALT isfox1.6in Debian), you can now specify the Debian package name explicitly with--debian-pkg. The command also tries to look up the Debian package name via Repology API when the direct lookup fails. Example:zoryn gen watch --debian-pkg fox1.6up: auto-detect merge strategy —
zoryn upnow automatically detects the appropriate update strategy based on repository structure:- Recursive (standard merge) — for repositories where working tree contains upstream source code (like php, curl, nginx)
- Ours (
-s ours) — for "pristine tarball + patches" workflow where working tree contains only build artifacts (.gear/,*.spec,*.patch) while upstream code is in git history (like libssh2, openssl) Subtree (
git subtree pull) — for repositories where upstream code lives in a subdirectory, detected by analyzing commit history for "Merge commit" pattern characteristic ofgit subtree pull --squash(like apache2-mod_auth_openidc)up: batch build with specsubst —
After the main build succeeds, zoryn automatically builds all subpackages by setting.gear/version-upnow supports[batch]section for packages that build multiple subpackages using gear's specsubst feature:git config --local gear.specsubst.<name> <value>for each value and running the configured build command. Build progress is shown for each subpackage with timing information. Git config is cleaned up after completion.config: configurable changelog template — changelog entry format for
Supported variables:zoryn upcan now be customized via~/.zorynconfiguration file:{old_version}— previous version{new_version}— new version{cves}— CVE fixes in format "(Fixes: CVE-...)" or empty string
Default template: - updated from {old_version} to {new_version} {cves} (trailing whitespace is trimmed automatically)
Refactor¶
- tests: migrate to Alcotest framework — all test files now use the Alcotest testing library instead of a custom hand-written test harness. This provides:
- Better test output with colored pass/fail indicators
- Automatic test grouping and statistics
- Standard test discovery and running via
dune runtest - Consistent assertion API (
Alcotest.check,Alcotest.fail)
Migrated test files: test_version.ml, test_rpm_spec.ml, test_alt_branches.ml, test_config.ml, test_batch_config.ml, test_girar.ml, test_rdb.ml, test_gau_git.ml, test_changelog.ml, test_version_mapper.ml, test_gear_utils.ml, test_upstream_detect.ml, test_validation.ml, test_opam_parser.ml, test_spec_gen.ml, test_gau_http.ml, test_watch.ml, test_cli_help.ml, version_test/test_version.ml (Version.compare)
- CLI: complete cmdliner migration — all command modules now use declarative cmdliner-based CLI:
main.ml— main command group (simplified, no wrapper functions)cmd_tag.ml— tag operations (find)cmd_spec.ml— spec operations (find, version, add changelog)cmd_build.ml— build commandcmd_check.ml— check operations (version, upstream)cmd_up.ml— version update from upstreamcmd_task.ml— task operations (replace, download, delete, genbatch, etc.)cmd_gen.ml— file generation (opam2spec, watch)
Benefits: - Automatic --help generation with man-page formatting - Automatic --version handling - Better error messages for invalid arguments - Consistent option handling across all subcommands - Examples section in help output
0.6.0 (2026-01-01)¶
New Features¶
- zoryn: unified CLI tool — new single-command interface for all package maintenance tasks, replacing multiple standalone utilities. Inspired by git's subcommand structure.
Commands: - zoryn up — automatic version update from upstream (git merge or tarball) - zoryn spec version [set <v>] — show/set version in spec file - zoryn spec find — find spec file in gear repository - zoryn spec add changelog — add changelog entry with optional CVE scanning - zoryn build — commit, tag, push and build package on gyle - zoryn task replace — replace subtask in existing task - zoryn task download <pkg> — download RPMs from task to hasher repo - zoryn task delete <id> <pkg> — delete subtask from task - zoryn task genbatch <id> — generate batch config from task - zoryn task refresh <id> — refresh stale subtasks - zoryn task batch <config> — batch build from config file - zoryn task rebuild <target> — rebuild dependent packages - zoryn check version — check for new upstream version - zoryn check upstream — scan for upstream source methods - zoryn tag find <version> — find git tags matching version - zoryn gen opam2spec — generate RPM spec from opam file - zoryn gen watch --debian — fetch watch file from Debian
Global options: - -d, --debug — enable debug output - -v, --verbose — enable verbose output - -C <dir> — change to directory before running - --version — show version
Migration from old tools: - gear_version_up → zoryn up - altgear build → zoryn build - altgear replace → zoryn task replace - altgear rebuild → zoryn task rebuild - altgear batch → zoryn task batch - altgear task download → zoryn task download - altgear task genbatch → zoryn task genbatch - altgear task delsub → zoryn task delete - altgear task refresh → zoryn task refresh - rpm_version_extractor → zoryn spec version - rpm_version_updater → zoryn spec version set - gear_find_spec → zoryn spec find - add_changelog → zoryn spec add changelog - git_tag_finder → zoryn tag find - opam2rpmspec → zoryn gen opam2spec - debian2watch → zoryn gen watch --debian - version_checker → zoryn check version - upstream_scanner → zoryn check upstream
Bash completions: completions/zoryn.bash
- version_checker: check for new upstream versions — new utility that checks ALT Linux packages for available upstream updates using data from upstream_scanner:
- Loads all package versions from RDB in one request (efficient)
- Scans upstream_scanner output directory for packages with watch/git info
- Watch file support — parses Debian-style watch files to find new versions
- Git tags support — fetches tags via
git ls-remotefor packages with upstream_git - Smart tag filtering by package name to avoid false positives from unrelated tags
- Parallel processing using OCaml 5 Domains
- Auto-saves results to input directory:
updates.txt— packages with newer versions (TSV format)failures.txt— packages where check failed with diagnostics (TSV format)
Usage: version_checker [OPTIONS] <input_dir> - -b, --branch BRANCH — repository branch (default: sisyphus) - -j, --jobs N — number of parallel workers (default: 8) - -p, --package PKG — check single package (for testing) - -v, --verbose — verbose output - -d, --debug — debug output
- upstream_scanner: scan ALT Linux packages for upstream sources — new utility that scans ALT Linux packages to find upstream source tracking methods:
- Gets list of all source packages from RDB API
- Fetches
.gear/rulesfrom gitweb to find watch file references - Extracts VCS URLs from spec files (
#Vcs-Git:,URL:tags) - Extracts upstream git URLs from
.gear/upstream/remotes - Saves found upstream info to directory structure:
<first_letter>/<package>/ - Parallel processing using OCaml 5 Domains for ~7x speedup with 8 workers
- Smart caching — tracks each repository's last commit date via gitweb atom feed. On subsequent runs, packages that haven't been updated since the last scan are skipped entirely. Progress shows
(cached)for packages with results and(cached-)for packages without upstream info - Multi-location support — automatically tries both
gearsandsrpmsgit repositories, caching the working location per package for efficiency
Usage: upstream_scanner [OPTIONS] <output_dir> - -b, --branch BRANCH — repository branch (default: sisyphus) - -c, --cache DIR — cache directory (default: same as output_dir) - -j, --jobs N — number of parallel workers (default: 8) - -p, --package PKG — scan single package (for testing) - -l, --limit N — limit to first N packages - -v, --verbose — verbose output - -d, --debug — debug output
- gear_version_up: tarball-based update workflow — automatic version updates for packages without upstream git access (like opari2). When
.gear/watchfile exists, the tool automatically: - Parses Debian-style watch file to find download URL pattern
- Fetches available versions from the watch URL
- Downloads the newest tarball with SHA256 checksum verification
- Imports tarball using
gear-updatewith correct target directory from.gear/rules - Commits with URL and checksum in message
- Updates spec version, changelog, and runs build
The update scheme is auto-detected based on git history and .gear/rules analysis: - Watch file in rules AND main tar is plain directory (no @version@) → tarball workflow (complex packages like nginx with tarball main + git modules) - Git history has merge commits → git merge workflow (even if watch file exists) - Git history has import/gear-update commits → tarball workflow - Watch file present but no clear history → tarball workflow
Use --tarball / -t flag to force tarball workflow even when git merge is detected.
- lib/watch: version fetching and download — extended watch module with:
fetch_versions— fetch available versions from watch URLfind_newest_version— compare versions and find newestdownload_file— download tarball with SHA256 checksumextract_version_from_filename— extract version from URL using watch patternrpm_spec: get_name function — new function to extract package name from spec file
lib/watch: Debian watch file parser — new module for parsing Debian-style watch files (version=3, 4, and 5 formats) used to track upstream releases. Reference: https://manpages.debian.org/experimental/devscripts/debian-watch.5.en.html
Supported formats: - Version=2: Legacy URL PATTERN format (deprecated since 2002, for compatibility) - Version=3: Simple URL PATTERN format with unquoted opts - Version=4: opts="..." URL PATTERN format with line continuation - Version=5: RFC822-style Key: Value format with full feature support
Macros (expanded in patterns): - @ANY_VERSION@ — version number regex - @SEMANTIC_VERSION@ — semver format (MAJOR.MINOR.PATCH) - @STABLE_VERSION@ — pure digit versions - @ARCHIVE_EXT@ — archive extensions (tar.gz, tar.xz, zip, etc.) - @SIGNATURE_EXT@ — signature extensions (asc, gpg, sig) - @DEB_EXT@ — Debian-specific extensions - @PACKAGE@ — package name - @COMPONENT@ — component name (for multi-tarball)
Mangle rules (sed-style s/pattern/replacement/flags): - dversionmangle — normalize Debian version (strip +dfsg suffixes) - uversionmangle — normalize upstream version - oversionmangle — transform .orig.tar.gz version - dirversionmangle — normalize directory paths - filenamemangle — transform filenames - downloadurlmangle — transform download URLs - pgpsigurlmangle — build PGP signature URL - pagemangle — normalize page before parsing - versionmangle — shorthand for both dversion and uversion - Transliteration rules: tr/from/to/ and y/from/to/ (Perl-style) - Case-insensitive flag: s/pattern/replacement/i - Escaped delimiters: patterns like s/.+\/v/ are parsed correctly - Multiple rules: separated by semicolons s/foo/bar/;s/baz/qux/
Mode settings: - Mode: LWP|git|svn — download/access mode - Pgp-Mode: auto|mangle|next|previous|self|gittag|none — signature verification - Search-Mode: html|plain — search in href or full page - Git-Mode: shallow|full — git clone strategy
Compression and repacking: - Compression: xz|gzip|bzip2|lzma|default - Repack: yes — force tarball repacking - Repacksuffix: +dfsg — suffix when repackaging
Git-specific options: - Pretty: 0.0~git%cd.%h — version format for HEAD - Date: %Y%m%d — date format - Git-Export: default|all — export mode - Git-Modules: all|<list> — submodules to clone
Component support (multi-tarball packages): - Component: <name> — secondary tarball name - Ctype: nodejs|perl — component type
Other features: - hrefdecode: percent-encoding — decode URL encoding - Decompress: yes — decompress before verification - Case-insensitive key names with hyphen normalization (e.g., Pgp-Sig-Url-Mangle → pgpsigurlmangle) - Multiple rules separated by semicolons - Line continuation with backslash
- lib/upstream_detect: update scheme detection — new module that analyzes git history to determine whether a package uses git-merge or tarball-based update workflow:
- Detects merge-tag commits (git-based workflow like mc, wireshark)
- Detects import/gear-update commits (tarball-based workflow like opari2)
- Git history takes priority over watch file presence (wireshark has watch file but uses git-merge workflow)
- Complex package support (nginx): when
.gear/ruleshascopy: *.watchAND maintar:directive is a plain directory (no@version@), tarball workflow is detected even if git history has merge commits from other components (modules) - Checks for watch file references in .gear/rules and spec file
Falls back to VCS tag analysis in spec file
gear_utils: tar directive analysis — new functions to analyze
.gear/rules:tar_line_uses_version_template— check if a tar line uses@version@main_tar_uses_version_template— check the first tar directive formatget_tar_directory— extract target directory forgear-updateDistinguishes between
tar: dirname(tarball import) andtar: v@version@:.(git-based workflow)gear_utils: watch file discovery — new functions to find watch files:
parse_watch_from_copy_line— extract watch file path fromcopy: *.watchlinesfind_watch_file_from_rules— find watch file referenced in.gear/rulesfind_watch_file— unified watch file search with priority:- Watch file from
.gear/rulesviacopy: *.watch .gear/watch(default location)debian/watch
- Watch file from
Supports glob patterns like
copy: *.watch(expands tonginx.watchetc.)debian2watch: fetch watch files from Debian — new CLI tool to download
debian/watchfiles from Debian Sources and save as.gear/watch:- Queries Debian Sources API for package info
- Downloads raw watch file via /data/ endpoint
- Handles lib* packages correctly (uses libX/ section prefix)
- Automatically adds
copy: .gear/watchto.gear/rules - Automatically adds
Source999: watchto spec file - Supports
--print/-poption to preview without saving - Detects existing watch files — checks
.gear/watch,.gear/rulesforcopy: *.watchpatterns, and spec file forSource: *.watch - Compares with Debian version — if a watch file already exists, shows both versions side-by-side and suggests updating if they differ
--force/-fflag — force update existing watch file with Debian version- Example:
debian2watch opari2creates.gear/watchfor tarball-based updates - Example:
debian2watch nginxdetects existingnginx.watchand compares with Debian Example:
debian2watch --force nginxupdates existing watch file from Debiangau_common.Io: read_file function — new function to read entire file content as a single string (complement to existing
read_lines)lib/changelog: nginx CHANGES format support — CVE extraction now supports nginx CHANGES file format with version headers like
Changes with nginx X.Y.Z. Previously only supported PHP NEWS format.
Breaking Changes¶
- config: renamed config file from
~/.gear-version-upto~/.zoryn— the global configuration file has been renamed to match the new tool name. Users should rename their existing config file:mv ~/.gear-version-up ~/.zoryn
Refactor¶
- version: consolidate version to single source — version is now defined only in
dune-projectand auto-generated intolib/version/version.mlduring build. Removed hardcoded versions frommain.mlandgau_http.ml. UseVersion.versionandVersion.user_agentto access version info programmatically.
Documentation¶
- README: restructured documentation — moved Configuration section right after Quick Start for better discoverability. Added note about navigating to package directory before running zoryn commands.
Fixes¶
security: fix shell injection in hook execution — added
Gau_common.Validationmodule with safe shell argument escaping. Hook paths and version arguments are now properly escaped using single-quote wrapping with embedded quote handling. Affected functions:run_hooks_from_dir,run_merge_hooks_from_dirincmd_up.ml.fix file handle leaks — replaced
open_in/close_inwithIn_channel.with_open_textinrpm_spec.mlandconfig.mlto ensure files are closed even on exceptions. Affected functions:get_version_info,get_name,get_vcs_tag,get_release,get_epoch,get_packager,read_value.fix infinite recursion in macro expansion — added cycle detection and depth limiting to
expand_macrosinrpm_spec.ml. Cyclic macro definitions (e.g.,%define a %{b}with%define b %{a}) now return unexpanded macros instead of causing stack overflow. Maximum expansion depth limited to 100.gau_git: replace failwith with Result type —
set_remotenow returns(unit, string) resultinstead of raisingFailure. This enables better error handling in calling code.add validation.mli interface — added missing interface file for the
Gau_common.Validationmodule with full documentation.refactor: extract common CVE code — moved CVE extraction logic from
rpm_spectochangelogmodule. Addedhas_cveandextract_cve_idsfunctions tochangelog.rpm_specnow uses these instead of duplicating.refactor: create gau_http library — extracted HTTP fetching code into new
lib/gau_http/library using libcurl. Bothrdbandwatchmodules now use this common implementation instead of duplicating curl logic.tests: improve test coverage — added tests for previously untested modules:
test_config.ml— 18 tests for INI config parsertest_opam_parser.ml— 21 tests for opam file parsingtest_gau_http.ml— 10 tests for HTTP clienttest_spec_gen.ml— 24 tests for spec file generationrefactor: extract magic constants — replaced hardcoded numbers with named constants for better readability and maintainability:
validation.ml: max_version_length, max_tag_length, max_package_name_length, max_path_lengthgau_http.ml: default_timeout, default_buffer_size, debug_truncate_lengthwatch.ml: download_timeoutalt_branches.ml: base_priorityupstream_detect.ml: default_commit_limitrefactor: eliminate HTTP client duplication — replaced 4 duplicate
fetch_urlimplementations incmd_check.ml,cmd_task.ml,cmd_gen.ml(2 places) with calls toGau_http.fetch_url_opt. Added?verify_sslparameter toGau_httpfunctions for cases requiring disabled SSL verification (gyle servers).refactor: centralize config file path — moved
get_config_file()fromgirar.mlandrdb.mltoGau_common.Config.default_config_file().refactor: remove unused variables and options — removed
_debugfromcmd_gen.ml, unused-j/--jobsoption fromzoryn check version, and unimplemented-b/-joptions fromzoryn check upstream.refactor: precompile regex patterns — moved
Re.compileandStr.regexpcalls from inside functions to module level incmd_task.mlandcmd_check.mlfor better performance.refactor: remove unimplemented batch mode — removed batch mode stubs from
zoryn check versionandzoryn check upstream. Both commands now require-p <package>option.lib/rdb: add User-Agent header to HTTP requests —
fetch_urlnow sends a proper User-Agent header (gear-autoupdate/1.0) to avoid being blocked by servers that filter requests without User-Agent (like git.altlinux.org gitweb).all CLI tools: consistent --help/-h and unknown option handling — all binary utilities now properly handle
--helpand-hflags, showing usage information. Unknown options now produce an error message instead of being silently ignored.gear_find_spec,rpm_version_updater,rpm_version_extractor— added --help/-h supportgit_tag_finder,gear_version_up,add_changelog— added -h as alias for --helpgear_version_up— now rejects unknown arguments instead of ignoring themaltgear— now rejects unknown options (starting with-) with an error messageopam2rpmspec: prefer stable versions over pre-releases when detecting version from git tags — when a repository has both stable (e.g.,
2.5.0) and pre-release tags (e.g.,2.5.0-alpha1,2.5.0-rc1), the tool now correctly selects the stable version instead of the pre-release. Also ignores ALT-specific packaging tags like2.5.0-alt1. The logic is now inGau_git.find_latest_stable_tagwith full test coverage
New Features¶
altgear rebuild: accept task ID as argument — when the argument is a number, it is used directly as the task ID instead of searching for a task by package name. The
--deps-by-pkgoption now takes a package name argument to query dependencies by package name instead of by task. Example:altgear rebuild 401209 --deps-by-pkg imatchuse alt_releases_matrix for branch hierarchy data — replaced hardcoded branch lists and hierarchy with data from the
alt_releases_matrixlibrary. This ensures branch information stays in sync with the official ALT Linux definitions.- Uses external
alt_releases_matrixpackage (ocaml-alt-releases-matrix-devel) - Added
lib/alt_branches— wrapper module providing convenient functions:get_parents,get_level,get_priority,sort_by_level,is_root, etc. - Updated
altgearto useAlt_branchesfor branch hierarchy and defaults - Updated
lib/girarto useAlt_branches.get_priorityfor repository comparison - Updated
lib/rdbto useAlt_branches.normalize_for_rdb - Updated bash completions with branches and architectures from the library
Added
test/test_alt_branches.mlwith 39 tests covering all functionsopam2rpmspec: comment out missing build dependencies — when build dependencies are not found in Sisyphus or local hasher, they are now added as commented
#BuildRequires:lines in the spec file, making it easy to see what needs to be packaged firstopam2rpmspec: parse dune-project to find packages without .opam files — some projects (like opam itself) define packages in
dune-projectthat don't have corresponding.opamfiles. These are typically meta-packages that contain binaries (e.g.,/usr/bin/opam). Now these extra packages are included in%dune_build -pand%dune_installcommands. Example: opam project has(package (name opam))in dune-project but noopam.opamfile.opam2rpmspec: map conf- packages to system dependencies* — opam
conf-*packages are virtual packages that check for system libraries, not OCaml packages. Now correctly mapped to system dependencies (e.g.,conf-openssl→libssl-devel,conf-pkg-config→pkgconfig,conf-diffutils→diffutils)opam2rpmspec multi-package support — handle projects with multiple opam files
- Auto-detects multiple
.opamfiles in directory (e.g.,opam-core.opam,opam-client.opam) - Generates single RPM spec with subpackages for each opam file
- Finds main package name from common prefix of opam file names
- Generates
%package -n ocaml-<name>and%package -n ocaml-<name>-develfor each - Merges and deduplicates BuildRequires from all packages
- Recognizes internal dependencies (e.g., opam-client depending on opam-core) and skips them in RDB check
- Single
%buildand%installsections with all package names - Per-package
%filessections using dune-generated file lists - Works with
--clonefor projects like opam, dune, etc. Example:
opam2rpmspecin opam directory generates spec for all 8 packagesopam2rpmspec — convert opam files to RPM spec files for ALT Linux
- Uses
opam-file-formatlibrary for reliable opam file parsing - Generates spec files with proper ALT Linux OCaml macros (
%dune_build,%dune_install, etc.) - Automatically generates
.gear/ruleswith correct tag format (auto-detectsv@version@vs@version@) - Automatically detects test dependencies and generates
%checksection with%def_with check - Gets version from git tags instead of opam file (opam versions are often outdated)
- Maps opam package names to RPM package names (e.g.,
re→ocaml-re-devel) - Converts SPDX license identifiers to RPM license format
- Extracts VCS URL from
dev-repofield - Gets packager name from git config if not specified
- Checks build dependencies in Sisyphus (via RDB API) and local hasher, reports missing packages
- Generates
%def_without checkwith comments listing missing packages when test deps are unavailable --clone <package>— clone package from opam repository:- Looks up package in opam-repository via GitHub API
- Finds the latest stable version (ignores alpha/beta/rc)
- Clones git repository from
dev-repotoocaml-<package>directory - Checks out the latest version tag to a
sisyphusbranch - Generates spec file in the cloned repository
- Smart naming: doesn't add
ocaml-prefix if package already starts withocaml-
--build— rungear-update-tag -a -cand build withgear-hshafter generation- Uses build command from
~/.gear-version-upconfig (defaults togear-hsh) - Saves build log to
.gear/build-log - Use
--verboseto show build output in real-time
- Uses build command from
- Supports
--no-checkto disable test section generation - Supports
--no-rulesto skip.gear/rulesgeneration - Supports
--no-rdb-checkto skip dependency checking via RDB - Supports
--tag-prefixto override tag prefix (e.g.,--tag-prefix ""for@version@) - Supports
-Vto override version (uses git tags by default, falls back to opam file) - Bash completion for all options and
.opamfiles - Example:
opam2rpmspec patch.opamoropam2rpmspec --clone patch
0.4.0 (2025-12-24)¶
New Features¶
- altgear task delsub — delete subtask from task by package name
Supports various formats:
pkg,pkg.git,pkg.git=tagaltgear rebuild --deps-by-pkg — query dependencies by package name instead of task ID
- Uses RDB API
/api/package/what_depends_srcinstead of/api/task/what_depends_src Works with NEW/BUILDING tasks that are not yet tested
Configurable RDB API URL —
[rdb] api_urloption in~/.gear-version-upDefault:
https://rdb.altlinux.org/apialtgear replace --tag — specify subtask tag when multiple exist
- Shows error with list of subtasks if ambiguous
Example:
altgear replace --tag=8.0-alt1altgear batch multi-branch support — build for multiple repositories at once
- Use comma-separated list:
-b sisyphus,p11,p10,c10f2,c9f2 - Creates separate task for each branch
- Automatically sets dependencies between tasks (p11 waits for sisyphus, etc.)
- Builds packages only once, adds to all branch tasks
Example:
altgear batch php -b sisyphus,p11,p10altgear task refresh — refresh stale subtasks with newer versions from repository
- Compares subtask versions with current repository state
- Detects subtasks where repository has newer version (updated during task wait time)
- Only checks
rebuildsubtasks (skips gear/srpm builds which are user-controlled) - Correctly handles packages with epoch (e.g.,
winewith1:10.18.1-alt1) - Deletes stale subtasks and adds rebuild from current repo version at the same position
--dry-runmode shows what would be done without making changes- Example:
altgear task refresh 403550 --dry-run
Improvements¶
- altgear rebuild: show pre-build statistics before processing
- Packages already in task
- Previously failed (with error logs)
- Remaining packages to build
Fixes¶
- altgear rebuild: check task state before RDB query (NEW/BUILDING not supported)
- altgear rebuild: skip packages already in task (using RDB API for accurate detection of all subtask types)
- altgear rebuild: proper CTRL+C handling — now correctly stops the loop when interrupted
- altgear batch: proper CTRL+C handling — now correctly stops the loop when interrupted
- altgear batch: create tags for non-main packages when using
--no-build - Previously, only main package tag was created, other packages failed with "tag name not found"
0.3.0¶
New Commands¶
- altgear batch — build chains of related packages from config files
- Supports specsubst for gear tags
- Package filtering by repository (exclude/only)
--create-taskoption to create task automatically--no-buildto skip local builds--dry-runto preview actionsaltgear task download — download built RPMs from task to local hasher repo
altgear task genbatch — generate batch config from existing task via RDB API
altgear rebuild — rebuild dependent packages from SRPMS
New Features¶
- add_changelog --auto — auto-generate changelog message with CVE info
- RPM-style version comparison — proper rpmvercmp algorithm for version sorting
- Automatic merge conflict resolution —
use-theirsoption in .gear/version-up - Version tag filtering —
filteroption to limit tags by captured values - PHP NEWS format support in changelog parser
Improvements¶
- Batch task detection now matches by tag, not just package name
- Bash completion for all utilities with directory navigation
- Better task ls parsing (handle
[locked]flag, empty try count) - Automated test suite for core modules
Internal¶
- Refactored to use OCaml 5.x standard library features
- Code formatted with ocamlformat
- Unified CLI utilities in gau_common.Cli module
0.2.0¶
- Initial public release
- altgear build/replace commands
- gear_version_up with upstream tag support
- RDB API client for build reason detection
- Repository hierarchy for automatic --deps