tl;dr: Add xml:base=https://path/prefix to an RPM package location metadata to tell DNF where to download RPMs from.
If you setup files on disk to match the URL structure on the download site, createrepo will include the file path in the repo metadata automatically.
Kena nerd sniped
I rarely need Zoom but when I need it, I need it. I’ve been conditioned to not use the web client for things like “I need to screenshare” and “I don’t like Chrome chewing up 100% of my CPU”.
And I’ve gotten tired of discovering that Zoom needs an update exactly when joining a meeting. (Honestly, props to Zoom for supporting 9 Linux flavours and a generic tar if you’re not using one of the 9.)
Then a friend mentioned he had a similar issue as well (amusingly while joining the same meeting), and then I started wondering how I could make it work for both of us.
Alternatives
I’m an SRE at $DAYJOB, I really don’t want to build and maintain something, especially if it’s just solving a minor annoyance. But I didn’t find anything with a cursory search other than people asking if such a repo existed back in 2021.
I could put dnf install -y https://zoom.us/client/latest/zoom_x86_64.rpm into a systemd timer, but that feels pretty janky. As part of my earlier search I stumbled across Zoom’s Linux client changelog which showed that release timing was inconsistent at best, continuously downloading & installing the package regardless of if it needed to be updated is wasteful. (In hindsight I could have used some of the things I wrote to make a smarter shell script though.)
Poking around Zoom’s official Linux install guide found no auto upgrading link for any Linux flavour, so it’s not necessarily a case of “Zoom dislikes Fedora”.
I did find a Flatpak, but that was a non-starter because it was repackaged by… someone?
“NOTE: This wrapper is not verified by, affiliated with, or supported by zoom.us.”
The closest alternative that I found was someone who rolled their own Zoom repo for private use and put it on S3. I wanted this to run on my VPS instead since these RPMs are chonky, and cloud storage adds up - especially file downloads.
Guess I have to roll my own
I know enough to be dangerous here. I had some past history with Copr and RPM repos. My idea was essentially:
- Cronjob that scrapes the current zoom URL & downloads the latest version RPM
- Cronjob that runs createrepo & mangles paths as necessary
- Be fancy: nginx config that 302 redirects any *.rpm requests to the proper Zoom download URL
#1 was straightforward - the Github repo generator had a “determine latest version” check. Adapting it to download the RPM into a specific folder is straightforward:
BASE_DIR=/opt/zoomrepo
LATEST=$(curl -s -I HEAD https://zoom.us/client/latest/zoom_x86_64.rpm | grep '^location:' | sed -e 's@^.*/prod/\([.0-9]*\)/zoom_x86_64.rpm.*$@\1@')
curl -L https://zoom.us/client/${LATEST}/zoom_x86_64.rpm -o $BASE_DIR/zoom_${LATEST}_x86_64.rpm
#2 was far easier than expected. A simple 1 liner: createrepo ${BASE_DIR}
Then things got interesting.
A different direction
When spot-checking the generated files, I saw a line <location href="zoom_6.1.6.1013_x86_64.rpm"/>. Going down a rabbithole, turns out there’s no official RPM repo spec. But even if there’s no spec to read, I could at least read the manpage to see if I could fiddle with the setting.
One option jumped out at me: baseurl. I recognised it as one of the options in a yum repo file, but usually as the remote path, so I wasn’t sure what it did here. I tried setting the option with createrepo --baseurl "https://zoom.us/client/" ${BASE_DIR} and checked the resulting metadata file:
<location xml:base="https://zoom.us/client/" href="zoom_6.1.6.1013_x86_64.rpm"/>
That looks really close to the actual download URL, and the repo metadata is just gzip-ed XML; I know Python and it has an XML editor. Could I fiddle around with the repo file to construct the proper path and completely avoid needing nginx to handle the redirects?
But then a potentially easier solution came to me - what if I just change the layout of the files on disk to match the download URLs? I know the official URLs are of the form zoom.us/client/${VERSION}/zoom_x86_64.rpm, I’m already downloading the files to a specific location, it’s easy enough to change the output path…
I created a directory and put the downloaded RPM in it, and it appeared that the proper URL would be formed from concatenating the base and href elements:
<location xml:base="https://zoom.us/client/" href="6.1.6.1013/zoom_x86_64.rpm"/>
I slapped together a basic DNF repo metadata file in /etc/yum.repo.d and tried downloading the file:
# dnf info zoom
Last metadata expiration check: 0:00:52 ago on Sun 04 Aug 2024 06:23:14 AM CEST.
Available Packages
Name : zoom
Version : 6.1.6.1013
Release : 1
Architecture : x86_64
Size : 191 M
Source : zoom-6.1.6.1013-1.src.rpm
Repository : zoom
Summary : Zoom Cloud Meetings
URL : https://www.zoom.us
>snip<
# dnf download zoom
Last metadata expiration check: 0:00:10 ago on Sun 04 Aug 2024 06:23:14 AM CEST.
zoom_x86_64.rpm 191 MB/s | 191 MB 00:01
Uh… unexpected success! I’m not used to things just working, particularly where I felt like I was abusing features.
What I ended up with
I ended up with a simpler design than what I originally planned. There’s no automated file editing, the most complicated thing is a sed construct to extract a version number. Less to support is always good: The core logic is an 8 line bash script that shellcheck doesn’t complain about, I don’t need to worry about Python versions, pyenvs, etc.
I also save bandwidth by directing all RPM download traffic to the Zoom CDN. Once I get it a bit more polished I can probably point more people at it, but in the meantime I’ve got it working on my laptops.
There were 4 artifacts:
- A bash script that checks for new Zoom clients, downloads the released RPMs to appropriate places on disk, then invokes
createrepo - A bash script that downloads a hardcoded list of 5 client versions to seed the repo (which will go out of date really quickly…)
- A repo file that I can drop into
/etc/yum.repos.don each device that I want to update Zoom - A nginx config drop in