Skip to content
zoryn/ maintainer-assistant

Changelog

0.37.0 - 2026-04-22

Security

  • up (hooks): zoryn up now 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, or runner=direct in ~/.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; --continue no longer inherits --no-sandbox or 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/sh trick 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 all target to gyle in a single SSH call so stale client-side subtask state no longer yields subtask #N not found cascades before the real subtasks are reached. Also warn when --revoke is combined with -m (gyle ignores the message). Closes #68.
  • task refresh: stop picking stale repo snapshots on girar — get_last_done_task_id sorted candidate tasks by task_id descending and picked the first DONE one, but girar does not commit tasks in task_id order (a task with a higher id can finish before one with a lower id). The reference snapshot could therefore predate already-committed newer versions, so task refresh reported "all packages up to date" even when subtasks were clearly behind repo. Now sort by task_changed descending (with task_id desc as a tiebreaker) to pick the latest-committed DONE task.
  • task manage: no more "ghost symbols" on the subtask-detail and dependencies headers — the colored #task_id / Sub #sub_id -- STATE -- repo -- owner overlay 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 #id overlay 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 used String.sub on byte offsets).
  • Shell mock layer (tests): SSH commands now require a fixture when ZORYN_TEST_FIXTURES is set — previously the test harness silently fell back to real ssh when 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 clear Shell fixture missing error. 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=version items 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 #NNN token 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}/; this CHANGELOG.md at the repo root is the single source for the "Changelog" page (symlinked from docs/en/changelog.md; RU falls back to EN via mkdocs-static-i18n).
  • ci: .forgejo/workflows/deploy-site.yaml — builds the MkDocs site and publishes site/ to Forgejo Pages on every push to master touching site/, docs/, mkdocs.yml, overrides/, or dune-project. The landing's version badge is baked from dune-project at build time by overrides/hooks.py — one source of truth for the displayed version. Closes #28.
  • up: new [tarball] subdir config field — subdirectory name inside the tarball to extract (passed as gear-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 with mozilla-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 bb and ba values (with long-form aliases pkgbinary and pkgall) — run rpmbuild -bb --short-circuit / -ba --short-circuit in the existing chroot to repackage only (Processing files → rpm, or src.rpm + rpm), reusing the %prep/%build/%install artifacts from the previous build. Useful when iterating on %files, packaging macros, or metadata without rerunning the full pipeline through hsh.

Changed

  • test-rebuild log-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-rebuild to reuse the existing chroot instead of a clean hsh build, 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 --skip still apply only to --all-subtasks and 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 --deps on tasks from a different build chain. Girar.is_repo_fresher used a flat branch_sorting_order index, so p11 (p-chain) was ranked "fresher" than c10f2 (c-chain) and find_upstream_tasks fed those tasks into --deps. It now consults Alt_branches.get_build_predecessors, which understands the real chains sisyphus → p11 → p10 → p9 → p8 and sisyphus → c10f2 → c9f2. For c10f2 only sisyphus is considered upstream; p11/p10 tasks are ignored. (#65)
  • build --section install: drop --nocheck from the rpmbuild arguments. ALT Linux rpmbuild does not accept --nocheck (it's a hsh-level option), so zoryn build --section install failed immediately with --nocheck: unknown option. The flag was also redundant: --short-circuit tells rpmbuild to jump straight to the requested stage, so -bi --short-circuit runs only %install and %check is 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 commit command — native replacement for gear-commit, extracts changelog from spec file as commit message; supports --spec, -m, --no-edit, --amend, -a flags. By default opens the message in the same editor git commit uses (resolved via git 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 — so ZZ, :wq, :x on 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"), :cq aborts with non-zero exit. For non-vim editors falls back to git commit -F file --edit, which has standard git semantics. Exports GEAR_COMMIT and GEAR_COMMIT_AMEND env vars for git hooks compatibility

Changed

  • task rebuild / batch rebuild: local SRPM lookup is now stricter — only files whose release starts with alt are considered. Previously the legacy Hasher.find_srpm accepted 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 no alt prefix at all) could be matched by name. After the unification onto Builder.find_srpm_in_path (which shares its matcher with the existing Builder.find_srpm used everywhere else), such files are silently ignored and task rebuild will 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 rebuild only consumes ALT-tagged SRPMs (this matches the behaviour Builder.find_srpm has always used for remote builders). Package names are now also validated through Gau_common.Validation.validate_package_name before 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_srpm used an unanchored regex (Re.eos only, no Re.bos), so a search for qt6-webengine would substring-match inside dqt6-webengine-6.10.2-alt0.dde.1.src.rpm and return the wrong file. The duplicate path-based finder has been removed: both bin/zoryn/cmd_task_rebuild.ml and lib/batch_pipeline/batch_pipeline.ml now use the new Builder.find_srpm_in_path, which shares its fully-anchored matcher (^pkg-…$) with Builder.find_srpm. The "no src.rpm found" warning in task rebuild was 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-dir instead of the hardcoded .git/zoryn-up-state.json path, so zoryn up (and the build pipeline it drives) works inside a git worktree. Previously the top-level .git in a worktree is a regular file (a gitdir: pointer), not a directory, so writing the state file failed with ENOTDIR. The new path lives in the per-worktree git directory (.git/worktrees/<name>/zoryn-up-state.json), which is also naturally isolated — two parallel zoryn up runs in two worktrees of the same package no longer collide. (#60)
  • up: scheme detection on packages whose .gear/upstream/remotes exists (or whose spec carries only a Url: to a public forge, no VCS:) no longer requires the maintainer to add the local upstream remote by hand and no longer takes minutes on repos with hundreds of merge commits. stage_detect now resolves the upstream URL from .gear/upstream/remotes → spec Vcs: → spec Url: (with Git_url.normalize + is_github_or_gitlab_url filter) 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, so vendor/tools/phobos/etc. conventions are honored; (2) failing that, it respects an existing upstream remote 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 it git remote add upstream <url> from the determined URL. Fetches use --tags without --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 from refs/remotes/<upstream>/* (so a maintainer-side packaging tag with the same name does not poison the result), and run a single git 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_fetch now reuses the same safe helper as a fallback when stage_detect could not determine a URL, and skips the redundant remote-setup step when the URL is already in state.upstream_url. (e.g. microsoft/onnxruntime: scheme detection went from ~30s of slow tag walking to a single merge-base call.)
  • 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-watch when the maintainer uses non-standard merge commit messages such as Merge 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 from refs/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), spec Vcs:/Url:, or a local remote named upstream/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's Version:-bump history with --follow --topo-order, verifies ancestry to skip meaningless ranges after rebase, and handles octopus merges. is_github_or_gitlab_url explicitly 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 .spec lives inside .gear/ (e.g. python3-module-hypothesis).
  • up: PyPI tag lookup now finds tags whose .gear/version-up pattern carries a decorative prefix (e.g. hypothesis-python-{major:+}.{minor:+}.{patch:+}) without needing an explicit template; [version] filter is still respected. (#57)
  • gen version-up: when run from .gear/ or any subdirectory of a git repository, write .gear/version-up to the repository root instead of creating a nested .gear/.gear/version-up. The -C/--chdir option still defines the working directory explicitly. (#59)
  • builder add: --hasher-dir was ignored in --multi-add mode; 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) hid ocaml-lzma because it does not yet exist in p11
  • gen pypi2spec: emit VCS: tag when the repository URL comes only from home_page (no project_urls.Repository), e.g. fasttext.
  • gen pypi2spec: --url now overrides any URL discovered in PyPI metadata (was: only used as a fallback) and is reflected in the spec's VCS: line.
  • gen pypi2spec: normalise PyPI repository URLs through a shared Gau_common.Git_url helper — handles git+https:/git: prefixes, trailing slashes, and .git suffix 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-ftbfs to skip per-architecture build tasks where the package is already FTBFS
  • TAB completion for package names from Repoteka API in clone, check, task, and gen watch commands

Fixed

  • task ls/show: fixed missing package names for delete/copy/rebuild subtasks — used pkgname field from Tasks API instead of absent package

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, y to copy original text (without wrapping) to clipboard via OSC 52 (#52)
  • tui: Space (page down) and b (page up) keys in log viewer — matching standard less keybindings (#50)
  • Syntax highlighting for log output using sublime-syntax engine (#46)
  • build: --section flag to run a specific rpmbuild section (prep, build, install, check) in an existing chroot with automatic spec file sync, --rpmbuild-args for passing extra rpmbuild arguments — for iterative build development and debugging
  • build: configurable log filename template log_filename in [build] config section (~/.zoryn). Supported variables: {builder}, {batch}, {pkgname}. Default: build.{batch}.{builder}.log
  • build, up: --skip-check now supports rpmbuild value to skip %check section via --rpmbuild-args='--without=check'; bare --skip-check without value skips everything (rpmbuild + post-build checks) (#45)
  • gen pypi2spec: %pyproject_runtimedeps_metadata for upstream runtime dependencies (#48)
  • gen pypi2spec: AutoReq: yes, nopython3 to disable python3 autoreq (#48)
  • gen pypi2spec: %global _unpackaged_files_terminate_build 1 in 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 .err log files for parallel builds — stderr is now written to the main log
  • gen pypi2spec: spec filename changed from python3-module-<name>.spec to <normalized-pypi-name>.spec (e.g. .gear/hdbscan.spec) (#48)
  • gen pypi2spec: Url: tag now always uses https://pypi.org/project/<name>/ instead of homepage from metadata (#48)
  • gen pypi2spec: removed %doc line 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_name to 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 submit after 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 up and zoryn check version now 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 --tag to 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 with task manage
  • tui_logview: new shared library for log viewer logic (classifier, word wrap, caching, search) — used by both task manage and task test-rebuild
  • zoryn task abort command — abort a running task by ID (with TAB completion for task IDs)
  • zoryn task approve / zoryn task disapprove commands — approve or disapprove task subtasks from CLI (by subtask number, package name, or all), with optional -m message and --revoke
  • Changelog CVE parser: support CVE:YYYY-NNNNN format (used by ISC Kea/BIND) — normalized to standard CVE-YYYY-NNNNN
  • Changelog version header: support Product X.Y.Z (status) released on Date format (ISC ChangeLog)
  • Tests for Io.read_package_list_file (path traversal, comments, blank lines, errors)
  • Tests for Custom_packages.collect (combined --package/--packages-file logic)
  • Tests for List_utils.chunk with size <= 0
  • Test for Stats.create ~custom_packages:[] normalization to None
  • Extract Custom_packages.collect into test_rebuild library for testability
  • task test-rebuild: --package PKG (repeatable) and --packages-file FILE flags to rebuild a custom set of packages instead of the full RDB dependency tree

Changed

  • task manage: log viewer logic extracted to shared tui_logview library (no user-visible change)
  • task test-rebuild --top: event loop migrated from Unix.select to 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 recognizing freeipmi- as prefix — now strips v/V prefix before matching
  • task test-rebuild --top: pressing [f] to view failures on the completion screen did nothing — render always showed the completion box ignoring view_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_release now accepts branch releases (alt1.M110P.1) and architecture-specific releases (alt1_e2k1) — previously only digits and dots were allowed after alt, causing release to be merged into version and breaking version comparison
  • task test-rebuild: --continue-no-refresh now updates packages_to_rebuild in 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_quiet mode appended `