#compdef aptly ################################################################################ # Copyright (C) 2018 Maximilian Stein # Acknowledgement: Many texts where copied from aptly(1) # # This project is licensed under the terms of the MIT license. # See file LICENSE for details. ################################################################################ # zsh completion script for Aptly (http://aptly.info/) ################################################################################ # see https://man.cx/aptly local curcontext="$curcontext" state line cmd subcmd ret=1 local arch_list=(amd64 arm64 armel armhf i386 mips mipsel mips64el ppc64el s390x) local bool="bool:{_wanted -V values expl 'bool' compadd true false}" local dists=({wheezy,jessie,stretch}{,-updates,-backports,-backports-sloopy} buster sid experimental) local components=(main contrib non-free) local aptly_query="aptly package query: " local aptly_format="aptly package display format: " local aptly_uploaders="-uploaders-file=[uploaders.json to be used when including .changes into this repository]:uploaders file:_files -g '*.json'" local keyring="*-keyring=[gpg keyring to use when verifying Release file (could be specified multiple times)]:keyring file:_files -g '*.gpg'" # complete command (( $+functions[_aptly-cmd] )) || _aptly-cmd() { _values "aptly command category" \ "mirror[manage, update mirrors of remote repositories]" \ "repo[manage local package repositories, add, remove, move, copy packages]" \ "snapshot[create, merge, manage snapshots]" \ "package[perform operation on the whole collection of packages]" \ "publish[publish snapshot or local repository]" \ "db[cleanup database and package pool, recover database after failure]" \ "task[multi-command tasks]" \ "serve[quickly serve published repositories via HTTP]" \ "config[configuration management]" \ "graph[generate dependency graph]" \ "api[REST API service]" ret=0 } # complete subcommand (( $+functions[_aptly-subcmd] )) || _aptly-subcmd() { local cmd=$1 case $cmd in mirror) _values "mirror commands" \ "create[create new mirror of remote repository]" \ "list[show full list of mirrors]" \ "show[show details about a mirror]" \ "drop[delete a mirror]" \ "update[update a mirror]" \ "rename[change name of a mirror]" \ "edit[change settings of a mirror]" \ "search[search mirror for packages matching query]" ret=0 ;; repo) _values "repo commands" \ "add[add packages to local repository]" \ "copy[copy packages between local repositories]" \ "create[create local repository]" \ "drop[delete local repository]" \ "edit[edit properties of local repository]" \ "import[import packages from mirror to local repository]" \ "list[list local repositories]" \ "move[move packages between local repositories]" \ "remove[remove packages from local repository]" \ "show[show details about local repository]" \ "rename[renames local repository]" \ "search[search repo for packages matching query]" \ "include[add packages to local repositories based on .changes files]" ret=0 ;; snapshot) _values "snapshot commands" \ "create[create snapshot of mirror or local repository]" \ "list[list snapshots]" \ "show[show details about snapshot]" \ "verify[verify dependencies in snapshot]" \ "pull[pull packages from another snapshot]" \ "diff[show difference between two snapshots]" \ "merge[merge snapshots]" \ "drop[delete snapshot]" \ "rename[rename snapshot]" \ "search[search snapshot for packages matching query]" \ "filter[filter packages in snapshot producing another snapshot]" ret=0 ;; publish) _values "publish commands" \ "drop[remove published repository]" \ "list[list published repositories]" \ "repo[publish local repository]" \ "snapshot[publish snapshot]" \ "switch[update published repository by switching to new snapshot]" \ "update[update published local repository]" \ "show[shows details of published repository]" ret=0 ;; package) _values "package commands" \ "search[search for packages matching query]" \ "show[show details about packages matching query]" ret=0 ;; db) _values "db commands" \ "cleanup[cleanup db and package pool]" \ "recover[recover db after crash]" ret=0 ;; serve) # no subcommand here _arguments '1:: :' \ '-listen=[host:port for HTTP listening]:host\:port: ' ret=0 ;; api) _values "api commands" \ "serve[start api http service]" ret=0 ;; graph) # no subcommand here _arguments '*:' \ '-format=[render graph to specified format]:image format:(png svg pdf)' \ '-layout=[create a more vertical or more horizontal graph layout]:layout:(horizontal vertical)' \ '-output=[specify output filename, default is to open result in viewer]:output file:_files' ret=0 ;; config) _values "config commands" \ "show[show current aptly config]" ret=0 ;; task) _values "task commands" \ "run[run aptly tasks]" ret=0 ;; esac } # complete parameters (( $+functions[_aptly-param] )) || _aptly-param() { local cmd=$1 subcmd=$2 local config=$opt_args[-config] [[ -n $config ]] && config="-config=$config" # get list of mirrors, or ' ' if none get_mirrors() { # retrieve list of mirrors local mirrors=($(aptly $config mirror list -raw=true 2>/dev/null)) # a single space causes just the help text to be shown [[ -z $mirrors ]] && mirrors=" " || mirrors="($mirrors)" echo $mirrors } # get lists of repos or ' ' if none get_repos() { # retrieve repo list local repos=($(aptly $config repo list -raw=true 2>/dev/null)) [[ -z $repos ]] && repos=" " || repos="($repos)" echo $repos } # get list of snapshots or ' ' if none get_snapshots() { local snapshots=($(aptly $config snapshot list -raw=true 2>/dev/null)) [[ -z $snapshots ]] && snapshots=" " || snapshots="($snapshots)" echo $snapshots } # get list of gpg keys or ' ' if none get_gpg_key_ids() { local gpg_keys=($(gpg --quiet --batch --keyid-format long --list-secret-keys --with-colons 2>/dev/null | grep '^sec' | cut -d ':' -f 5)) [[ -z $gpg_keys ]] && gpg_keys=" " || gpg_keys="($gpg_keys)" echo $gpg_keys } ret=0 case $cmd in mirror) local mirrors=$(get_mirrors) case $subcmd in create) _arguments \ "-filter=[filter packages in mirror]:$aptly_query" \ "-filter-with-deps=[when filtering, include dependencies of matching packages as well]:$bool" \ "-force-architecture=[(only with architecture list) skip check that requested architectures are listed in Release file]:$bool" \ "-force-components=[(only with component list) skip check that requested components are listed in Release file]:$bool" \ "-ignore-signatures=[disable verification of Release file signatures]:$bool" \ $keyring \ "-with-sources=[download source packages in addition to binary packages]:$bool" \ "-with-udebs=[download .udeb packages (Debian installer support)]:$bool" \ "(-)2:new mirror name: " ":archive url:_urls" ":distribution:($dists)" "*:components:_values -s ' ' components $components" ;; list) _arguments '1:: :' \ "-raw=[display list in machine-readable format]:$bool" ;; show) _arguments \ "-with-packages=[show detailed list of packages and versions stored in the mirror]:$bool" \ "(-)2:mirror name:$mirrors" ;; drop) _arguments \ "-force=[force mirror deletion even if used by snapshots]:$bool" \ "(-)2:mirror name:$mirrors" ;; update) _arguments \ "-download-limit=[limit download speed (kB/s)]:kB/s: " \ "-downloader=[downloader to use]:str: " \ "-force=[force update mirror even if it is locked by another process]:$bool" \ "-ignore-checksums=[ignore checksum mismatches while downloading package files and metadata]:$bool" \ "-ignore-signatures=[disable verification of Release file signatures]:$bool" \ $keyring \ "-max-tries=[max download tries till process fails with download error]:number: " \ "-skip-existing-packages=[do not check file existence for packages listed in the internal database of the mirror]:$bool" \ "-latest=[download only latest version of each package (per architecture)]:$bool" \ "(-)2:mirror name:$mirrors" ;; rename) _arguments \ "2:old mirror name:$mirrors" ":new mirror name: " ;; edit) _arguments \ "-filter=[filter packages in mirror]:$aptly_query" \ "-filter-with-deps=[when filtering, include dependencies of matching packages as well]:$bool" \ "-with-sources=[download source packages in addition to binary packages]:$bool" \ "-with-udebs=[download .udeb packages (Debian installer support)]:$bool" \ "(-)2:mirror name:$mirrors" ;; search) _arguments \ "-format=[custom format for result printing]:$aptly_format" \ "-with-deps=[include dependencies into search results]:$bool" \ "(-)2:mirror name:$mirrors" ":$aptly_query" ;; esac ;; repo) local repos=$(get_repos) local create_edit=("-comment=[any text that would be used to described local repository]:comment: " "-component=[default component when publishing]:component:($components)" "-distribution=[default distribution when publishing]:distribution:($dists)" $aptly_uploaders ) case $subcmd in add) _arguments \ "-force-replace=[when adding package that conflicts with existing package, remove existing package]:$bool" \ "-remove-files=[remove files that have been imported successfully into repository]:$bool" \ "(-)2:repo name:$repos" "*:package files:_files -g '*.{udeb,deb,dsc}'" ;; copy) _arguments \ "-dry-run=[don’t copy, just show what would be copied]:$bool" \ "-with-deps=[follow dependencies when processing package−spec]:$$bool" \ "(-)2:src repo name:$repos" ":dest repo name:$repos" "*:$aptly_query" ;; create) local snapshots=$(get_snapshots) _arguments \ ${create_edit[@]} \ "(-)2:new repo name: " \ "3:::('from')" "4:::('snapshot')" "5::snapshot:$snapshots" ;; drop) _arguments \ "-force=[force local repo deletion even if used by snapshots]:$bool" \ "(-)2:repo name:$repos" ;; edit) _arguments \ ${create_edit[@]} \ "(-)2:repo name:$repos" ;; import) local mirrors=$(get_mirrors) _arguments \ "-dry-run=[don’t import, just show what would be imported]:$bool" \ "-with-deps=[follow dependencies when processing package−spec]:$bool" \ "(-)2:src mirror name:$mirrors" ":dest repo name:$repos" "*:$aptly_query" ;; list) _arguments '1:: :' \ "-json=[display list in JSON format]:$bool" \ "-raw=[display list in machine−readable format]:$bool" ;; move) _arguments \ "-dry-run=[don’t move, just show what would be moved]:$bool" \ "-with-deps=[follow dependencies when processing package−spec]:$bool" \ "(-)2:srv repo name:$repos" ":dest repo name:$repos" "*:$aptly_query" ;; remove) _arguments \ "-dry-run=[don’t remove, just show what would be removed]:$bool" \ "(-)2:repo name:$repos" "*:$aptly_query" ;; show) _arguments \ "-json=[display record in JSON format]:$bool" \ "-with-packages=[show list of packages]:$bool" \ "(-)2:repo name:$repos" ;; rename) _arguments \ "2:old repo name:$repos" ":new repo name: " ;; search) _arguments \ "-format=[custom format for result printing]:$aptly_format" \ "-with-deps=[include dependencies into search results]:$bool" \ "(-)2:repo name:$repos" ":$aptly_query" ;; include) _arguments '1:: :' \ "-accept-unsigned=[accept unsigned .changes files]:$bool" \ "-force-replace=[when adding package that conflicts with existing package, remove existing package]:$bool" \ "-ignore-signatures=[disable verification of .changes file signature]:$bool" \ $keyring \ "-no-remove-files=[don’t remove files that have been imported successfully into repository]:$bool" \ "-repo=[which repo should files go to, defaults to Distribution field of .changes file]:repo name:$repos" \ $aptly_uploaders \ "(-)*:changes files/directories:_files -g '*.changes'" ;; esac ;; snapshot) local snapshots=$(get_snapshots) case $subcmd in create) local mirrors=$(get_mirrors) local repos=$(get_repos) _arguments -C \ '(-)2:new snapshot name: ' \ '3: :->src1' \ '4:: :->src2' '5:: :->src3' case $state in src1) _values 'snapshot src' 'from' 'empty' ;; src2) if [[ $line[3] == from ]]; then _values 'snapshot src' 'mirror' 'repo' fi ;; src3) if [[ $line[3] == from ]]; then case $line[4] in mirror) _arguments "5:mirror name:$mirrors" ;; repo) _arguments "5:repo name:$repos" ;; esac fi ;; esac ;; list) _arguments '1:: :' \ "-raw=[display list in machine−readable format]:$bool" \ "-sort=[display list in ’name’ or creation ’time’ order]:sort order:((name\:'alphabetical order' time\:'chronological order'))" ;; show) _arguments \ "-with-packages=[show list of packages]:$bool" \ "(-)2:snapshot name:$snapshots" ;; verify) _arguments '1:: :' \ "(-)2:snapshot name:$snapshots" "*::more snapshots:$snapshots" ;; pull) _arguments \ "-all-matches=[pull all the packages that satisfy the dependency version requirements]:$bool" \ "-dry-run=[don’t create destination snapshot, just show what would be pulled]:$bool" \ "-no-deps=[don’t process dependencies, just pull listed packages]:$bool" \ "-no-remove=[don’t remove other package versions when pulling package]:$bool" \ "(-)2:to snapshot name:$snapshots" "3:src snapshot name:$snapshots" "4:new dest snapshot name: " \ "*:$aptly_query" ;; diff) _arguments \ "-only-matching=[display diff only for matching packages (don’t display missing packages)]:$bool" \ "(-)2:snapshot name a:$snapshots" "3:snapshot name b:$snapshots" ;; merge) _arguments \ "-latest=[use only the latest version of each package]:$bool" \ "-no-remove=[don’t remove duplicate arch/name packages]:$bool" \ "(-)2:new dest snapshot name: " "*:source snapshot name(s):$snapshots" ;; drop) _arguments \ "-force=[remove snapshot even if it was used as source for other snapshots]:$bool" \ "(-)2:snapshot name:$snapshots" ;; rename) _arguments '1:: :' \ "2:old snapshot name:$snapshots" "3:new snapshot name: " ;; search) _arguments \ "-format=[custom format for result printing]:$aptly_format" \ "-with-deps=[include dependencies into search results]:$bool" \ "(-)2:snapshot name:$snapshots" ":$aptly_query" ;; filter) _arguments \ "-with-deps=[include dependent packages as well]:$bool" \ "(-)2:src snapshot name:$snapshots" "3:new dest snapshot name: " "*:$aptly_query" ;; esac ;; publish) # read lines of output into # format of each item: local -a publish_prefixes local -a publish_dists for line in ${(@f)"$(aptly $config publish list -raw=true 2>/dev/null)"} { publish_prefixes+=($line[(ws: :)1]) publish_dists+=($line[(ws: :)2]) } publish_prefixes_uniq=(${(@u)publish_prefixes}) publish_dists_uniq=(${(@u)publish_dists}) [[ -z $publish_prefixes_uniq ]] && publish_prefixes_uniq=" " || publish_prefixes_uniq="($publish_prefixes_uniq)" [[ -z $publish_dists_uniq ]] && publish_dists_uniq=" " || publish_dists_uniq="($publish_dists_uniq)" local gpg_keys=$(get_gpg_key_ids) # common options for publishing # TODO: is the keyring parameter correct? local publish_update_options=( "-batch=[run GPG with detached tty]:$bool" "-force-overwrite=[overwrite files in package pool in case of mismatch]:$bool" "-gpg-key=[GPG key ID to use when signing the release]:gpg key id:$gpg_keys" "-keyring=[GPG keyring to use (instead of default)]:keyring file:_files -g '*.gpg'" "-passphrase=[GPG passphrase for the key (warning: could be insecure)]:passphrase: " "-passphrase-file=[GPG passphrase−file for the key (warning: could be insecure)]:passphrase file:_files" "-secret-keyring=[GPG secret keyring to use (instead of default)]:secret-keyring:_files" "-skip-contents=[don’t generate Contents indexes]:$bool" "-skip-bz2=[don't generate bzipped indexes]:$bool" "-skip-signing=[don’t sign Release files with GPG]:$bool" ) local components_options=( "-component=[component name to publish (for multi−component publishing, separate components with commas)]:components:_values -s , components $components" ) local publish_options=( "-butautomaticupgrades=[set value for ButAutomaticUpgrades field]:$bool" "-distribution=[distribution name to publish]:distribution:($dists)" "-label=[label to publish]:label: " "-suite=[suite to publish]:suite: " "-codename=[codename to publish]:codename: " "-notautomatic=[set value for NotAutomatic field]:notautomatic: " "-origin=[origin name to publish]:origin: " ${components_options[@]} ) local endpoint_prefix="[endpoint\:]prefix" case $subcmd in repo) local repos=$(get_repos) _arguments \ ${publish_options[@]} \ ${publish_update_options[@]} \ "(-)2:repo name:$repos" "3::$endpoint_prefix: " ;; snapshot) local snapshots=$(get_snapshots) _arguments '1:: :' \ ${publish_options[@]} \ ${publish_update_options[@]} \ "(-)*:snapshot name:$snapshots" "3::$endpoint_prefix: " ;; switch) local snapshots=$(get_snapshots) _arguments \ ${publish_update_options[@]} \ ${components_options[@]} \ "(-)2:distribution:$publish_dists_uniq" "3::$endpoint_prefix:$publish_prefixes_uniq" \ "*:new snapshot name:$snapshots" ;; update) _arguments \ ${publish_update_options[@]} \ "(-)2:distribution:$publish_dists_uniq" "3::$endpoint_prefix:$publish_prefixes_uniq" ;; show) _arguments '1:: :' \ "(-)2:distribution:$publish_dists_uniq" "3::$endpoint_prefix:$publish_prefixes_uniq" ;; esac ;; package) case $subcmd in search) _arguments \ "-format=[custom format for result printing]:$aptly_format" \ "(-)2:$aptly_query" ;; show) _arguments \ "-with-files=[display information about files from package pool]:$bool" \ "-with-references=[display information about mirrors, snapshots and local repos referencing this package]:$bool" \ "(-)2:$aptly_query" ;; esac ;; db) case $subcmd in cleanup) _arguments '1:: :' \ "-dry-run=[don’t delete anything]:$bool" \ "-verbose=[be verbose when loading objects/removing them]:$bool" ;; recover) # nothing to complete... ;; esac ;; serve) # completed in _aptly-subcmd ;; api) case $subcmd in serve) _arguments '1:: :' \ "-listen=[host:port for HTTP listening or unix://path to listen on a Unix domain socket]:host\:port or unix\://path: " \ "-no-lock=[don’t lock the database]:$bool" ;; esac ;; graph) # completed in _aptly-subcmd ;; config) case $subcmd in show) # nothing to do ;; esac ;; task) case $subcmd in run) _arguments '1:: :' \ "(2)-filename=[specifies the filename that contains the commands to run]:filename:_files" \ "(-filename)*::comma-separated command list: " esac ;; esac } # main completion _arguments -C \ "-architectures=[list of architectures to consider (comma−separated), default to all available]:architectures:_values -s , architectures $arch_list" \ "-config=[location of configuration file]:file:_files -g '*.conf'" \ "-db-open-attempts=[number of attempts to open DB if it’s locked by other instance]:number:()" \ "-dep-follow-all-variants=[when processing dependencies, follow a & b if dependency is ’a|b’]:$bool" \ "-dep-follow-recommends=[when processing dependencies, follow Recommends]:$bool" \ "-dep-follow-source=[when processing dependencies, follow from binary to Source packages]:$bool" \ "-dep-follow-suggests=[when processing dependencies, follow Suggests]:$bool" \ "-dep-verbose-resolve=[when processing dependencies, print detailed logs]:$bool" \ "-gpg-provider=[PGP implementation]:gpg provider:((gpg\:'external gpg' internal\:'Go internal implementation'))" \ '(-)1: :->cmds' \ '2: :->subcmd' \ '*:: :->args' && ret=0 cmd=$line[1] subcmd=$line[2] case $state in cmds) _aptly-cmd _arguments '(-)1:: :((help\:integrated\ command\ help))' ;; subcmd) case $cmd in help) _aptly-cmd ;; *) _aptly-subcmd $cmd _arguments '(-)2:: :((help\:integrated\ command\ help))' ;; esac ;; args) # help anywhere in line? if [[ ${line[(i)help]} -le ${#line} ]]; then if [[ ${#line} -le 3 ]]; then if [[ $line[1] == help ]]; then _aptly-subcmd $subcmd elif [[ $line[2] == help ]]; then _aptly-subcmd $cmd fi fi else _aptly-param $cmd $subcmd # this somehow destroys parameter completion, so disable it for now #_arguments '(-)3:: :((help\:integrated\ command\ help))' fi ret=0 ;; esac return ret # mode: Shell-Script # sh-indentation: 4 # indent-tabs-mode: nil # sh-basic-offset: 4 # End: # vim: ft=zsh sw=4 ts=4 et