Changelog¶
Unreleased¶
Added¶
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).
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 `