Skip to content

Instantly share code, notes, and snippets.

@awerlang
Last active February 22, 2024 01:24
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save awerlang/b792a3f908206a90ad58ba559c5400bb to your computer and use it in GitHub Desktop.
Save awerlang/b792a3f908206a90ad58ba559c5400bb to your computer and use it in GitHub Desktop.
A zypper wrapper that maximizes network throughput
zypper-download
===============
Downloads packages using any amount of available openSUSE mirrors.
Installation
------------
Copy both files to the following locations:
$HOME/.config/aria2/aria2.conf
$HOME/bin/zypper-download
Make sure the program is executable:
chmod +x $HOME/bin/zypper-download
This tool leverages aria2 to be awesome, so make sure it is installed (use zypper one last time):
zypper in aria2
Usage
-----
It works best with repositories set to an official download redirector e.g. http://download.opensuse.org, since it relies on generated metalinks. So update your repositories URLs as needed.
Refer to instructions:
$ zypper-download -h
Known issues
------------
* Only supports HTTP/HTTPS/FTP protocols for repositories
* Cannot be passed an individual .rpm URL directly (use aria2c instead)
* Minor: Some third-party repositories (e.g. Visual Studio Code) follow a different directory structure which results in those packages being skipped
check-integrity=true
console-log-level=error
file-allocation=falloc
max-concurrent-downloads=40
#!/bin/bash
#####################################################################################################
##
### NAME
## zypper-download - Downloads packages using any amount of available openSUSE mirrors.
##
### SYNOPSIS
## Usage: zypper-download [options] command [command-args]...
##
## Options:
##
## -d <dir> Package cache directory. It can be an absolute path or relative to the
## current directory, or start with the environment variable ${HOME}.
## Default: $PKG_CACHE_DIR
## -z Run zypper once packages are ready in cache.
## -c Display default configuration.
## -h Display this message and exit.
## -v Display version information and exit.
##
## Commands:
##
## dup, dist-upgrade
## in, install package-name...
## inr, install-new-recommends
##
### CONFIGURATION
## PKG_CACHE_DIR = ${HOME}/.cache/zypp/packages
## RUN_ZYPPER = no
##
### LICENSE
## zypper-download v0.3.3
##
## Copyright (C) 2020-2022 André Werlang
##
## License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
## This is free software: you are free to change and redistribute it.
## There is NO WARRANTY, to the extent permitted by law.
##
#####################################################################################################
set -o errexit -o nounset -o noclobber -o pipefail
shopt -s nullglob
export LC_ALL=C
main() {
parse_opts "$@"
set_default_config
run
}
error() {
echo "$0: $*"
} 1>&2
parse_opts() {
local OPTIND OPTARG flag
while getopts hvczd: flag
do
case "$flag" in
(h) help; exit 0;;
(v) version; exit 0;;
(c) configuration | about; exit 0;;
(d) readonly PKG_CACHE_DIR="$OPTARG";;
(z) readonly RUN_ZYPPER="yes";;
(*) usage; exit 1;;
esac
done
shift $((OPTIND-1))
if [ $# -eq 0 ]; then
error "expected required argument -- command"
usage;
exit 1;
fi
readonly SUB_COMMAND=("$@")
}
read_default_config() {
configuration | awk '/[[:alnum:]_ *= *.*]/{ print "[[ -v " $1 " ]] || readonly " $1 $2 "\x27" $3 "\x27" }'
}
set_default_config() {
local vars
vars=$(read_default_config)
eval "$vars"
}
subst_vars() {
export PKG_CACHE_DIR
envsubst '$HOME $PKG_CACHE_DIR'
}
about() {
set_default_config
subst_vars
}
usage() {
sed '/^### SYNOPSIS$/,/^###/!d;//d;s/^## \{0,6\}//' "$0" | about
}
version() {
sed '/^### LICENSE$/,/^###/!d;//d;s/^## \{0,6\}//' "$0" | about
}
help() {
sed '/^##$/,/^####/!d;//d;s/^##.\{0,2\}//' "$0" | about
}
configuration() {
sed '/^### CONFIGURATION$/,/^###/!d;//d;s/^## \{0,6\}//' "$0"
}
package_dir() {
echo "$PKG_CACHE_DIR" | subst_vars
}
run() {
call_zypper "${SUB_COMMAND[@]}" \
| create_download_spec \
| tee ./zypper-download.log \
| download_all
check_rpm || { error "Package signature failed"; exit 1; }
if [[ "$RUN_ZYPPER" == yes ]]; then
local dir
dir=$(package_dir)
sudo zypper --pkg-cache-dir "$dir" "${SUB_COMMAND[@]}"
fi
}
call_zypper() {
local command="$1"
local command_args=("${@:2}")
sudo zypper --quiet \
--terse \
--non-interactive \
"$command" --details \
--dry-run \
"${command_args[@]}"
}
create_download_spec() {
local repo_arrays
repo_arrays=$(zypper repos --uri | awk -F "|" '
function trim(s) {
gsub(/^ +| +$/, "", s);
return s;
}
BEGIN {
print "BEGIN {";
}
/:\/\// {
alias=trim($2);
name=trim($3);
uri=trim($NF);
print " uri[\"" name "\"]=\"" uri "\";";
print " alias[\"" name "\"]=\"" alias "\";";
}
END {
print "}";
}
'
)
awk -v PKG_CACHE_DIR="$PKG_CACHE_DIR" "$repo_arrays"'
function trim(s) {
gsub(/^ +| +$/, "", s);
return s;
}
NF==0 {
pkg=0;
next;
}
/ packages? (is|are) going to (be installed|be reinstalled|be upgraded|be downgraded|change architecture):$/ {
pkg=1;
next;
}
pkg==1 {
name=$1; getline;
version=$NF; getline;
arch=$1; getline;
repo=trim($0); getline;
host=uri[repo];
print host "/" arch "/" name "-" version "." arch ".rpm";
print " dir=" PKG_CACHE_DIR "/" alias[repo] "/" arch;
}
' -
}
download_all() {
aria2c --input-file=- \
--max-connection-per-server=6 \
--split=16 \
--min-split-size=1M \
--check-integrity=true \
--allow-overwrite=true \
--auto-file-renaming=false \
--continue=true
}
check_rpm() {
local dir output
dir=$(package_dir)
output=$(find "$dir" -type f -name "*.rpm" -execdir rpm --checksig "{}" +)
local result=$?
if (( result != 0 )); then
grep " NOT OK$" <<< "$output"
fi
return $result
} 1>&2
main "$@"
@marozsas
Copy link

marozsas commented Jan 15, 2022

I got errors that I hope could help you to debug.
Please, note the errors, this time, are not the same as reported on reddit. Anyway... here, follows:

tinybit:~ # zypper lr --uri
Repository priorities in effect: (See 'zypper lr -P' for details)
90 (raised priority) : 1 repository
99 (default priority) : 6 repositories

| Alias | Name | Enabled | GPG Check | Refresh | URI

--+---------------------+---------------------------------------------+---------+-----------+---------+--------------------------------------------------------------------------------------
1 | google-chrome-beta | google-chrome-beta | Yes | (r ) Yes | Yes | http://dl.google.com/linux/chrome/rpm/stable/x86_64
2 | home_concyclic_java | Java-related packages (openSUSE_Tumbleweed) | Yes | (r ) Yes | No | https://download.opensuse.org/repositories/home:/concyclic:/java/openSUSE_Tumbleweed/
3 | microsoft-edge | microsoft-edge | Yes | (r ) Yes | Yes | https://packages.microsoft.com/yumrepos/edge/
4 | packman | packman | Yes | (r ) Yes | Yes | http://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/
5 | repo-non-oss | openSUSE-Tumbleweed-Non-Oss (20220113) | Yes | (r ) Yes | Yes | http://download.opensuse.org/history/20220113/tumbleweed/repo/non-oss/
6 | repo-oss | openSUSE-Tumbleweed-Oss (20220113) | Yes | (r ) Yes | Yes | http://download.opensuse.org/history/20220113/tumbleweed/repo/oss/
7 | repo-update | openSUSE-Tumbleweed-Update | Yes | (r ) Yes | Yes | http://download.opensuse.org/update/tumbleweed/
tinybit:~ #

The zypper-download.log can be found at https://paste.opensuse.org/36157840

thanks for any help,

@awerlang
Copy link
Author

@marozsas Took the zypper-download.log you provided and pass it on to aria2. There's a couple different errors, but it's just regular operation. I got about 3080 packages downloaded at the end.

Summary of errors:

  • Checksum error detected: aria2 checksums local files even when they don't yet exist. Can be ignored.
  • errorCode=3 Resource not found: some packages from packman had new versions published. zypper refresh and repeat.

About the original error you found, I suppose it's related to repo URIs which can be a source of error.

@rxng
Copy link

rxng commented Feb 25, 2022

Getting error messages on first run

02/25 14:10:47 [ERROR] Unrecognized URI or unsupported protocol: /noarch/yast2-network-4.4.40-1.1.noarch.rpm

02/25 14:10:47 [ERROR] Unrecognized URI or unsupported protocol: /noarch/yast2-security-4.4.12-1.1.noarch.rpm

02/25 14:10:47 [ERROR] Unrecognized URI or unsupported protocol: /noarch/info-lang-6.7-6.1.noarch.rpm

02/25 14:10:47 [ERROR] Unrecognized URI or unsupported protocol: /noarch/makeinfo-lang-6.7-6.1.noarch.rpm

@awerlang
Copy link
Author

@rxng Seems to be the same issue as @marozsas. Can you run and send these over:

zypper lr --uri
grep yast2 zypper-download.log

@marcushallett
Copy link

Hey, @awerlang. I'm getting the same error as @rxng.

@awerlang
Copy link
Author

awerlang commented Jun 7, 2022

Hey, @awerlang. I'm getting the same error as @rxng.

Hi @marcushallett, this is likely related to your repositories, send me the output of

zypper lr --uri
cat zypper-download.log

@marcushallett
Copy link

marcushallett commented Jun 23, 2022

Sorry for the delay, @awerlang.

From three weeks ago, this was the output.

Edit: Today it's giving me "No files to download." That's a different issue, I'll investigate that further a bit later. Needed a vendor change. Fixed this, but yields same problem as before, for which you asked for the information.

@awerlang
Copy link
Author

@marcushallett I published v0.3.2 which addresses when there are more than 9 repositories in the system. Please let me know if you find other issues.

@marcushallett
Copy link

marcushallett commented Nov 2, 2022

@awerlang I'm seeing a ton of checksum error messages when running zypper-download (zypper-download dist-upgrade --no-recommends)

@awerlang
Copy link
Author

awerlang commented Nov 2, 2022

@marcushallett at the end, is every package downloaded successfully? you can see that when zypper reports no more packages to download. If they do download, then there's no problem.

@schmidthausen
Copy link

@awerlang It seems to fail every time there is a package on the Packman repo on my laptop. It seems to miss the Extra or Multimedia directory which should be in between the openSUSE_Tumbleweed and x86_64. Do I need to add a separate repo file for each part of packman?

@awerlang
Copy link
Author

awerlang commented Jan 14, 2023

@awerlang It seems to fail every time there is a package on the Packman repo on my laptop. It seems to miss the Extra or Multimedia directory which should be in between the openSUSE_Tumbleweed and x86_64. Do I need to add a separate repo file for each part of packman?

@p4p4j0hn I use packman essentials without issues, but per your description the full packman repo uses a different url pattern. As you mention using separate repos might work.

To get it working out of the box there's a few alternatives:

  1. Hardcode fixes for such repos (full packman, vs code, etc)
  2. Parse the repositories metadata to find the correct location for each download
  3. If there's a python lib for that if would be great

@schmidthausen
Copy link

@awerlang I ended up adding a separate repo file for each section of Packman that I use. This allowed the script to find the packages.

I originally installed those packages using opi which only added the main repo. Zypper was able to update the applications for some reason, just not this script.

@TonyApuzzo
Copy link

Has there been any updates to this? I pretty much require packman and vscode so this is of limited utility without understanding how to workaround those limitations. I don't understand how to "add a separate repo file for each part of packman"

@awerlang
Copy link
Author

@TonyApuzzo see https://en.opensuse.org/Additional_package_repositories#Packman how to use separate packman repos. I updated the known issues to mention that it's not a big deal, it just skips packages (e.g. vs code)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment