Mirror a copr, all architectures and releases and versions

coprmirror is my yum mirror solution, with a wrapper to mirror specifically a named COPR repository.

Overview

COPR is a distro-run community offering that lets users build yum/dnf repositories with their own software (primarily if not exclusively in rpm format). COPR performs the builds, and then hosts the binary and source rpms for client machines. My project today downloads using native GNU/Linux tools the yum repositories that collectively make up a COPR, so each release-releasever-basearch triplet. The end goal is to have a local copy of the entire current yum repos. This does not copy the build assets or log files; just what a yum repository defines and also the gpg public key.

Notably this utility needs the yum python packages present only for evaluating yum variables in the inurl (baseurl from a .repo file), so if you have a string literal as the inurl value, you can run this on a system that does not have yum installed.

Using

Configure coprmirror.conf from the provided .example file, and then run:

COPRMIRROR_CONF=coprmirror.conf VERBOSE=1 DEBUG=1 ./coprmirror.sh

Upstream

Original content

The get_file function was improved after being imported from my Reference 1 script.

Alternatives

I felt like ansible and system are overkill, but if you like those, this is perfect for you: https://github.com/ganto/ansible-copr_reposync

Dependencies

wget, grep, awk, sed, jq

References

obsmirror
Mirror an OBS repository locally — update 1 [this blog]

Thoughts about yum repo server and defined repositories

The internal server used as a yum repository runs EL6, and it servers EL6 and EL7 (read: CentOS) yum repos. An admin tried installing mkisofs, which wanted to come by default from the c7-base repository. It also wanted to upgrade bash and glibc. Well, installing a post-usrmerge bash (CentOS 7+) on CentOS6 caused all sorts of havoc. I had to load a rescue iso, boot, and copy /usr/bin/bash and /usr/bin/sh to the correct locations. Then my system would actually boot again.

I was getting an interesting error:

init: Failed to spawn rcS pre-start process: unable to execute: No such file or directory
init: Failed to spawn rcS post-stop process: unable to execute: No such file or directory

Also, kernel options rghb and quiet are really annoying and I always disable them.

So, the moral of the story is: always be very careful running yum on a yum repo server. Double-check what repos your package will pull in.

List yum repos that have packages installed and are still defined

yum repolist all $( yum list installed | awk '$NF ~ /@/ && $NF !~ /anaconda/ {split($NF,a,"@");print a[2];}' | sort | uniq ) | awk 'NR > 2 && $1 !~ /repolist/ {split($1,a,"/");print a[1]}'

The idea is to list each installed package which also lists the repo it was installed from, and then organize those repos. Then list them and show the ones that are still defined in yum’s repository files.

reposync fails: “No more mirrors to try.”

Symptom

The following message occurs, when running a yum reposync.

# time /usr/bin/reposync --source --repoid=EL7 -m --download-metadata --download_path="/var/www/html/yum/EL/EL7_Mirror"
Repository hosting is listed more than once in the configuration
firefox-60.6.0-3.0.1.el7_6.src FAILED
firefox-60.6.0-3.0.1.el7_6.src: [Errno 256] No more mirrors to try.                            ]  0.0 B/s |    0 B  --:--:-- ETA<[pre>

This could occur during an ansible play.

TASK [sync preprod EL7 to prod] ******************************************************************************************************************
fatal: [yumserver01]: FAILED! => {"changed": true, "cmd": "/usr/bin/reposync --source --repoid=EL7 -m --download-metadata --download_path=\"/var/www/html/yum/EL/EL7_Mirror\"", "delta": "0:04:35.805969", "end": "2019-04-17 08:27:22.119744", "msg": "non-zero return code", "rc": 1, "start": "2019-04-17 08:22:46.313775", "stderr": "", "stderr_lines": [], "stdout": "Repository hosting is listed more than once in the configuration\n\rfirefox-60.6.1-1.0.1.el7_6.src FAILED                                          \n\r(1/34): firefox-60.6.0-3.0 0% [                 ]  0.0 B/s |    0 B   --:-- ETA \r\rfirefox-60.6.0-3.0.1.el7_6.src FAILED                                          \n\r(1/34): firefox-60.6.0-3.0 0% [                 ]  0.0 B/s |    0 B   --:-- ETA \r\r(1/34): freerdp-1.0.2-15.e 0% [                 ]  0.0 B/s | 2.5 MB   --:-- ETA \r\rfreerdp-1.0.2-15.el7_6.1.src.r FAILED                                          \n\r(1/34): freerdp-1.0.2-15.e 0% [                 ]  0.0 B/s | 2.5 MB   --:-- ETA \r\rfreerdp-1.0.2-15.el7_6.1.src.r FAILED                                          \n\r(1/34): freerdp-1.0.2-15.e 0% [                 ]  0.0 B/s | 2.5 MB   --:-- ETA \r\rfreerdp-1.0.2-15.el7_6.1.src.r FAILED                                          \n\r(1/34): freerdp-1.0.2-15.e 0% [                 ]  0.0 B/s | 2.5 MB   --:-- ETA \r\rfreerdp-1
.............................. TRUNCATED ..............................
 0.0 B/s | 2.5 MB   --:-- ETA ", "", "tzdata-java-2019a-1.el7.noarch FAILED                                          ", "", "(1/34): tzdata-java-2019a- 0% [                 ]  0.0 B/s | 2.5 MB   --:-- ETA ", "libwsman1-2.6.3-6.git4391e5c.el7_6.i686: [Errno 256] No more mirrors to try.", "openwsman-client-2.6.3-6.git4391e5c.el7_6.i686: [Errno 256] No more mirrors to try.", "tuned-utils-2.10.0-6.0.1.el7_6.3.noarch: [Errno 256] No more mirrors to try.", "openwsman-server-2.6.3-6.git4391e5c.el7_6.x86_64: [Errno 256] No more mirrors to try.", "sbd-1.3.1-8.2.el7_6.1.x86_64: [Errno 256] No more mirrors to try.", "libwsman1-2.6.3-6.git4391e5c.el7_6.x86_64: [Errno 256] No more mirrors to try.", "sbd-1.3.1-8.2.el7_6.1.src: [Errno 256] No more mirrors to try.", "freerdp-1.0.2-15.el7_6.1.x86_64: [Errno 256] No more mirrors to try.", "freerdp-plugins-1.0.2-15.el7_6.1.x86_64: [Errno 256] No more mirrors to try.", "openwsman-client-2.6.3-6.git4391e5c.el7_6.x86_64: [Errno 256] No more mirrors to try.", "ocfs2-tools-1.8.6-11.el7.x86_64: [Errno 256] No more mirrors to try.", "python-2.7.5-77.0.1.el7_6.src: [Errno 256] No more mirrors to try.", "tuned-2.10.0-6.0.1.el7_6.3.noarch: [Errno 256] No more mirrors to try.", "firefox-60.6.0-3.0.1.el7_6.src: [Errno 256] No more mirrors to try.", "freerdp-libs-1.0.2-15.el7_6.1.i686: [Errno 256] No more mirrors to try.", "freerdp-1.0.2-15.el7_6.1.src: [Errno 256] No more mirrors to try.", "freerdp-libs-1.0.2-15.el7_6.1.x86_64: [Errno 256] No more mirrors to try.", "ghostscript-9.07-31.el7_6.10.i686: [Errno 256] No more mirrors to try.", "ocfs2-tools-1.8.6-11.el7.src: [Errno 256] No more mirrors to try.", "tzdata-java-2019a-1.el7.noarch: [Errno 256] No more mirrors to try.", "ghostscript-9.07-31.el7_6.10.x86_64: [Errno 256] No more mirrors to try.", "openwsman-2.6.3-6.git4391e5c.el7_6.src: [Errno 256] No more mirrors to try.", "tuned-profiles-cpu-partitioning-2.10.0-6.0.1.el7_6.3.noarch: [Errno 256] No more mirrors to try.", "firefox-60.6.1-1.0.1.el7_6.src: [Errno 256] No more mirrors to try.", "thunderbird-60.6.1-1.0.1.el7_6.src: [Errno 256] No more mirrors to try.", "openwsman-server-2.6.3-6.git4391e5c.el7_6.i686: [Errno 256] No more mirrors to try.", "libssh2-1.4.3-12.el7_6.2.src: [Errno 256] No more mirrors to try.", "pcs-0.9.165-6.0.3.el7_6.1.x86_64: [Errno 256] No more mirrors to try.", "openwsman-python-2.6.3-6.git4391e5c.el7_6.x86_64: [Errno 256] No more mirrors to try.", "pcs-0.9.165-6.0.3.el7_6.1.src: [Errno 256] No more mirrors to try.", "tuned-2.10.0-6.0.1.el7_6.3.src: [Errno 256] No more mirrors to try.", "tzdata-2019a-1.el7.src: [Errno 256] No more mirrors to try.", "ghostscript-9.07-31.el7_6.10.src: [Errno 256] No more mirrors to try."]}
        to retry, use: --limit @/etc/ansible/retries/reposync-prod.retry
PLAY RECAP **********************************************************************************************************************************************
yumserver01               : ok=1    changed=0    unreachable=0    failed=1

Possible causes

The original rpm files could be malformed, so even though the reposync client is successfully downloading whatever the server provided, the rpm does not match the checksum in the yum metadata files.

Fix #1

  1. Run the command manually to learn the whole list. Save it down to a file.
    # time /usr/bin/reposync --source --repoid=EL7 -m --download-metadata --download_path="/var/www/html/yum/EL/EL7_Mirror"
    Repository hosting is listed more than once in the configuration
    firefox-60.6.0-3.0.1.el7_6.src FAILED
    firefox-60.6.0-3.0.1.el7_6.src: [Errno 256] No more mirrors to try.                            ]  0.0 B/s |    0 B  --:--:-- ETA

    The file used in the documented example consisted of about 30 packages.
    file: bad.in

    firefox-60.6.0-3.0.1.el7_6.src
    firefox-60.6.1-1.0.1.el7_6.src
    freerdp-1.0.2-15.el7_6.1.src
    freerdp-1.0.2-15.el7_6.1.x86_64
    freerdp-libs-1.0.2-15.el7_6.1.i686
    freerdp-libs-1.0.2-15.el7_6.1.x86_64
    freerdp-plugins-1.0.2-15.el7_6.1.x86_64
    ghostscript-9.07-31.el7_6.10.i686
    ghostscript-9.07-31.el7_6.10.src
  2. On the reposync client delete or move to a temp directory the failed files. You could use a shell script similar to the following.
    #!/bin/sh
    INFILE=/root/bad.in
    INDIR=/var/www/html/yum/EL/EL7_Mirror/EL7
    OUTDIR=/root/orig/
    mkdir -p "${OUTDIR}"
    find $(
    for word in $( cat "${INFILE}" ) ;
    do
       find "${INDIR}" -name "${word}*rpm"
    done
    ) -print -exec mv {} "${OUTDIR}" \;
    
  3. On the repo server, manually download the files. You could modify the input file to have the upstream URLs.
    bad.in.url

    http://upstream.example.com/repo/EL/EL7/latest/x86_64/getPackage/tuned-utils-2.10.0-6.0.1.el7_6.3.noarch.rpm
    http://upstream.example.com/repo/EL/EL7/latest/x86_64/getPackage/tzdata-java-2019a-1.el7.noarch.rpm
    http://upstream.example.com/repo/EL/EL7/latest/x86_64/getPackageSource/firefox-60.6.0-3.0.1.el7_6.src.rpm
    http://upstream.example.com/repo/EL/EL7/latest/x86_64/getPackageSource/firefox-60.6.1-1.0.1.el7_6.src.rpm
    http://upstream.example.com/repo/EL/EL7/latest/x86_64/getPackageSource/freerdp-1.0.2-15.el7_6.1.src.rpm
    .... TRUNCATED .....                                                                                                                                                     
    

    You could use a shell script similar to the following.

    #!/bin/sh
    INFILE=/root/bad.in.url
    INDIR=/var/www/html/yum/EL/EL7_Mirror/EL7/getPackage/
    INDIRSRC=/var/www/html/yum/EL/EL7_Mirror/EL7/getPackageSource/
     
    for word in $( cat "${INFILE}" ) ;
    do
       if echo "${word}" | grep -q getPackageSource ;
       then
          cd "${INDIRSRC}"
       else
          cd "${INDIR}"
       fi
       #echo "$( pwd ) --> ${word}"
       wget "${word}"
    done
  4. Rerun the reposync manually and make sure it works.

yum: curl error #63: callback aborted

Symptom

The following message occurs.

http://yum01.ad.example.com/yum/mirror/kernel/getPackage/kernel-doc-4.0.5-124.16.4.el7.noarch.rpm: [Errno 14] curl#63 - "Callback aborted"
Trying other mirror.
 
 
Error downloading packages:
  kernel-doc-4.1.12-124.16.4.el7.noarch: [Errno 256] No more mirrors to try.

Possible causes

When yum fails to download a file due to “curl error #63: Callback aborted,” that means that reposync downloaded the rpm file incorrectly. It doesn’t match the expected checksum in the metadata file.

Fix #1

  1. On the mirror server (probably yum01), delete the offending files and download them manually from upstream.
    #!/bin/sh
    urldir=http://yum.upstream.example.com/repo/EL7/Mirror/x86_64/getPackage/
    outdir=/var/www/html/yum/repo/EL7_Mirror/Mirror/getPackage
    cd "${outdir}"
    
    for word in kernel-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-debug-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-debug-devel-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-devel-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-doc-4.0.5-124.16.4.el7.noarch.rpm 
    kernel-firmware-4.0.5-124.16.4.el7.noarch.rpm;
    do
       /bin/rm -f "${word}"
       wget "${urldir}${word}"
    done
  2. On the squid proxy server (probably proxy01), purge the cached files.
    #!/bin/sh
    # reference:
    #time squidclient -h localhost -r -p 3128 -m PURGE http://yum01.ad.example.com/yum/Mirror/EL7/getPackage/kernel-4.0.5-124.16.4.el7.x86_64.rpm
    
    urldir=http://yum.upstream.example.com/repo/EL7/Mirror/x86_64/getPackage/
    outdir=/var/www/html/yum/repo/EL7_Mirror/Mirror/getPackage
    
    for word in kernel-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-debug-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-debug-devel-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-devel-4.0.5-124.16.4.el7.x86_64.rpm 
    kernel-doc-4.0.5-124.16.4.el7.noarch.rpm 
    kernel-firmware-4.0.5-124.16.4.el7.noarch.rpm;
    do
       squidclient -h localhost -r -p 3128 -m PURGE "${urldir}${word}"
    done

Setup Yum Repository with Security Metadata

Define repository

Prepare the repo file on the server, so clients can download it.

cd /var/www/html/yum
cat <<'EOF' > hosting.repo
[hosting]
name=Hosting Delivery
baseurl=http://yum5.ipa.example.com/yum/hosting/
enabled=0
gpgcheck=0
EOF

Make or update repository

Use createrepo tool to make the repository. A wrapper script for creating or updating the existing repository is shown here.

tf=/usr/local/bin/updaterepo.sh
cat <<'EOF' > "${tf}"
#!/bin/sh
# reference:
#    https://gitlab.com/bgstack15/mirror/blob/master/usr/share/mirror/examples/rpm/update-examplerpm.sh

# Prepare directory and files
test -z "${UR_REPODIR}" && UR_REPODIR=/var/www/html/yum/hosting
test -z "${UR_BASEURL}" && UR_BASEURL=http://yum5.ipa.example.com/yum/hosting
test -z "${UR_OWNERSHIP}" && UR_OWNERSHIP="root.root"
test -z "${UR_FILETYPES}" && UR_FILETYPES="rpm"

find "${UR_REPODIR}" -exec chown "${UR_OWNERSHIP}" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type f -exec chmod "0664" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type d -exec chmod "0775" {} + 1>/dev/null 2>&1
chmod 0754 "$0"
restorecon -RF "${UR_REPODIR}"

# Prepare repo for rpm
cd "${UR_REPODIR}"
createrepo -v -u "${UR_BASEURL}" --basedir "${UR_REPODIR}" --simple-md-filenames --no-database --update --pretty .
EOF

Run this script.

/usr/local/bin/updaterepo.sh

Manually make the security metadata

The security metadata that yum interprets is stored in updateinfo.xml.gz. To make this file and include it in repomd.xml, you need to prepare it and learn some information about it.

This is a trim example of updateinfo.xml. Please see the epel metadata for a full example. I do not have an automatic process for generating this file yet.

tf=updateinfo.xml
cat <<'EOF' > "${tf}"
<?xml version="1.0" encoding="UTF-8"?>
<updates>
  <update status="final" type="security" version="1" from="bgstack15@gmail.com">
    <id>HELP-210217</id>
    <title>bgscripts-core update</title>
    <release>Enterprise Linux 7</release>
    <issued date="2018-04-02"/>
    <rights>CC-BY-SA 4.0</rights>
    <description>bgscripts-core
[1.3-8]
- latest version from upstream
</description>
    <solution>This update is internal to the company.</solution>
    <references>
      <reference href="https://gitlab.com/bgstack15/bgscripts" type="self" title="bgscripts-core" />
    </references>
    <pkglist>
      <collection short="bgscripts">
        <name>bgscripts suite</name>
        <package name="bgscripts-core" version="1.3-8" release="" epoch="0" arch="noarch">
          <filename>bgscripts-core-1.3-8.noarch.rpm</filename>
          <sum type="md5">eaa20075720bf12d6e837a4f546241ab</sum>
        </package>
     </collection>
    </pkglist>
  </update>
</updates>
EOF

Update the repo metadata to include updateinfo.xml

A yum repository includes metadata of the package metadata, and stores this meta-metadata in repomd.xml. Insert the metadata for this new file, updateinfo.xml in the repomd file.
This script is an update version of updaterepo.sh, which was listed earlier in this document.

tf=/usr/local/bin/updaterepo.sh
        cat <<'EOF' > "${tf}"
#!/bin/sh
# reference:
#    https://gitlab.com/bgstack15/mirror/blob/master/usr/share/mirror/examples/rpm/update-examplerpm.sh

# Prepare directory and files
test -z "${UR_REPODIR}" && UR_REPODIR=/var/www/html/yum/hosting
test -z "${UR_BASEURL}" && UR_BASEURL=http://yum5.ipa.example.com/yum/hosting
test -z "${UR_OWNERSHIP}" && UR_OWNERSHIP="root.root"
test -z "${UR_FILETYPES}" && UR_FILETYPES="rpm"
test -z "${UR_UPDATEINFO_INPUT}" && UR_UPDATEINFO_INPUT=/var/www/html/yum/build-hosting-repo/updateinfo.xml

find "${UR_REPODIR}" -exec chown "${UR_OWNERSHIP}" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type f -exec chmod "0664" {} + 1>/dev/null 2>&1
find "${UR_REPODIR}" -type d -exec chmod "0775" {} + 1>/dev/null 2>&1
chmod 0754 "$0"
restorecon -RF "${UR_REPODIR}"

# Prepare basic repo
cd "${UR_REPODIR}"
createrepo -v -u "${UR_BASEURL}" --basedir "${UR_REPODIR}" --simple-md-filenames --no-database --update --pretty .

# Inject custom updateinfo
# this task assumes the repomd file does not include node <data type="updateinfo"> yet.
UR_repomd="${UR_REPODIR}/repodata/repomd.xml"
UR_updateinfo_gz_short="repodata/updateinfo.xml.gz"
UR_updateinfo_gz="${UR_REPODIR}/${UR_updateinfo_gz_short}"

if ! test -e "${UR_UPDATEINFO_INPUT}" ;
then
   # file is absent, so decide how to fail.
   :
else
   # file exists, so continue with custom injection

   # learn open-size and open-checksum
   UR_updateinfo_opensize="$( /usr/bin/stat -c "%s" "${UR_UPDATEINFO_INPUT}" )"
   UR_updateinfo_openchecksum="$( /usr/bin/sha256sum "${UR_UPDATEINFO_INPUT}" | awk '{print $1}' )"

   # compress file and learn size and checksum
   /usr/bin/gzip < "${UR_UPDATEINFO_INPUT}" > "${UR_updateinfo_gz}"
   UR_updateinfo_size="$( /usr/bin/stat -c "%s" "${UR_updateinfo_gz}" )"
   UR_updateinfo_checksum="$( /usr/bin/sha256sum "${UR_updateinfo_gz}" | awk '{print $1}' )"
   UR_updateinfo_timestamp="$( /usr/bin/stat -c "%Y" "${UR_updateinfo_gz}" )"

   # insert information into repomd
   this_string="<data type=\"updateinfo\">
  <checksum type=\"sha256\">${UR_updateinfo_checksum}</checksum>
  <open-checksum type=\"sha256\">${UR_updateinfo_openchecksum}</open-checksum>
  <location xml:base=\"${UR_BASEURL}\" href=\"${UR_updateinfo_gz_short}\"/>
  <timestamp>${UR_updateinfo_timestamp}</timestamp>
  <size>${UR_updateinfo_size}</size>
  <open-size>${UR_updateinfo_opensize}</open-size>
</data>"

   {
      sed -r -e '/<\/repomd>/d' "${UR_repomd}"
      printf "%s\n%s\n" "${this_string}" "</repomd>"
   } > "${UR_repomd}.$$"
   /bin/touch --reference "${UR_repomd}" "${UR_repomd}.$$"
   /bin/mv -f "${UR_repomd}.$$" "${UR_repomd}"
fi
EOF

Summary

Using bash to modify xml files is obviously not ideal. However, this xml file is simple enough so this ugly mechanism suffices. For teams that know how to manage custom yum repositories and also want to just use yum update –security, this process should be a good basis or even complete solution!

Appendices

Appendix A: http proxy

If you use an http proxy for your yum traffic, the proxy might cache old versions of the metadata or package files. A quick and dirty way to clean up a squid proxy of the metadata file follows.

time squidclient -h localhost -r -p 3128 -m PURGE http://yum5.ipa.example.com/yum/hosting/repodata/updateinfo.xml.gz

Squid unfortunately does not allow recursive purging, so you will have to loop over all the metadata files and any package files you want to ensure get cleared.

References

Local file /var/cache/yum/x86_64/7Server/epel/69b82df00108c0ac8ac82fafbd0f3b89cc98d8dfe4fa350af7a23331a878fea2-updateinfo.xml.bz2

Find which package provides a file – Yum and Apt

Last updated 2019-06-26

Yum-based distros

On Fedora-like systems, the default package manager is yum (or dnf for Fedora 22+). The main way you use it is to install packages and auto-resolve the dependencies.

yum install irfan

Similarly, apt-get on the Debian family of GNU/Linux installs packages with this command.

apt-get install irfan

Now, let’s say you are looking for which package installs a particular file. The command for yum is:

yum provides */filename

If you want to use rpm, that is available too.

rpm -qf /etc/sudoers

Apt-based distros

On apt-based distros (Debian, Devuan, Ubuntu, etc.), you need to install the package apt-file before you can do similar lookup commands.

apt-get install apt-file

Now you can search with:

apt-file search */filename

You can use the lower-level package manager:

dpkg-query -S /usr/share/filename

Update just one repository on Linux

So if you build your own packages (rpm/dpkg/other), you might have your own repository of packages for your systems. And if you want to tell your hosts to update just the one repository, you might be looking for a solution.

A user on Ask Ubuntu had a fantastic answer for Ubuntu/debian flavor: http://askubuntu.com/questions/65245/apt-get-update-only-for-a-specific-repository/197532#197532.

For those of you who don’t want to click through, here is a summary of the entries, collapsed down to just one file (which might not be the best solution, but it works).
Add these lines to ~/.bashrc:
update-repo() {
for source in "$@"; do
sudo apt-get update -o Dir::Etc::sourcelist="sources.list.d/${source}" \
-o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
done
}

_ppa_lists(){
local cur
_init_completion || return

COMPREPLY=( $( find /etc/apt/sources.list.d/ -name "*$cur*.list" \
-exec basename {} \; 2> /dev/null ) )
return 0
} &&
complete -F _ppa_lists update-repo

With rpm-based systems like RHEL/CentOS/Fedora, you are using yum or dnf. Here’s my dnf implementation:

_command_dnf=yum
which dnf 1>/dev/null 2>&1 && _command_dnf="$( which dnf 2>/dev/null )"

# update-repo command for dnf update just one repository
update-repo() {
case "${_command_dnf}" in
*dnf)
for source in "$@"; do
sudo "${_command_dnf}" check-update -q --refresh --disablerepo=* --enablerepo="${source}"
done
;;
*yum)
for source in "$@"; do
sudo "${_command_dnf}" clean metadata -q --disablerepo=* --enablerepo="${source}" -q; yum check-update -q --disablerepo=* --enablerepo="${source}"
done
;;
esac
}

# autocomplete for update-repo
_repo_lists() {
local cur
_init_completion || return
COMPREPLY=( $( grep -hoiE -- "^\[.*\]" /etc/yum.repos.d/* | tr -d '[]' | grep -E "^${2:-.*}" ) )
return 0
} &&
complete -F _repo_lists -o filenames update-repo

The functions whose names start with an underscore are the auto-complete commands. Who doesn’t like a good tab auto-completion? Now that I learned how to do my own bash-completion, I’ll be doing it a lot more!

Update yum repo with an easy script

Similar to how you can Build an apt repository on CentOS and update it with new packages with a simple script, you can do the same with a yum rpm repository.

cat <<'EOFUPDATE' > ./update-yumrepo.sh
#!/bin/sh

# working directory
repodir=/mnt/mirror/bgscripts/
cd ${repodir}
chmod 0644 *rpm 1>/dev/null 2>&1

# create the package index
createrepo .
EOFUPDATE
chmod u+x ./update-yumrepo.sh