Package for devuan: keyboard-leds-trayicons

I recently purchased a ThinkPad P50s and I love it! However, it doesn’t have indicators for capslock and numlock. So I wrote a tool to simulate them! Keyboard-leds-trayicons is my solution. You can go install it in Devuan Ceres from my OBS.

Reading the status of capslock and numlock is trivial on the command line:

xset q | awk 'function d2b(d,b) {while(d) {b=d%2b;d=int(d/2)}return(b)} /LED/{print d2b($NF)}'

The awk is there to convert the provided decimal value into a binary. But how do you poll this, and display it in your X11 session in an unobtrusive way? With a POSIX shell script, of course! But what about the actual icons? Use my fork of mktrayicon. My fork adds a few patches, and you can also get it at the OBS link above.

capslock and numlock indicators in system tray, both on

capslock and numlock indicators in system tray, capslock off and numlock on

Obviously most people have no need for this package. And by now anyone without the indicators probably doesn’t need them. This project was 99% for myself, but I like to share.

The default icons are a bit boring, which fits my style. But the tool uses the xdg icon spec, so you can add your own icons for capslock-on and similar. Patches are welcome to the project, so if you really think this package should include more icons, or use yours by default, please open a merge request!

Found: OBS service documentation

For some unknown reason, it is really hard to find good documentation on how to use the _service file for the Open Build Service.

The Open Build Service (OBS) is a project from the openSUSE team, and they run a public instance at This server application provides a build environment for many distributions’ package methods. I use it for my public Devuan packages (see my “OBS” link in the links section of this site, usually a sidebar on each page).

The _service file has some generic documentation in the main obs online man pages. I find this documentation extremely lacking in explaining how to use a service file fully. And I’ve learned by examples extant on the public OBS instance already.

So, just to be clear, a _service file is a file in an obs project, that defines a number of steps for the server to take. By default, each service runs for each commit, or whenever you select the “trigger services” button on the web page for a project.

I finally found a list of available service names to use! And the links take you to the projects on the Internet where they live and are fully documented. There are quite a few I plan on using in the future… like regex_replace.

Example _service file

With my service file set up to pull down the source debian/ directory as debian.tar.xz, and the entire source as a tar.gz, and extract out my .dsc file from that source, I can manage my OBS project way easier.

   <service name="tar_scm">
      <param name="scm">git</param>
      <param name="url"></param>
      <param name="subdir">debian</param>
      <param name="filename">debian</param>
      <param name="revision">local-dsc-file</param>
      <param name="version">_none_</param>
   <service name="recompress">
      <param name="file">*.tar</param>
      <param name="compression">xz</param>
   <service name="tar_scm">
      <param name="scm">git</param>
      <param name="url"></param>
      <param name="revision">local-dsc-file</param>
      <param name="version">_none_</param>
   <service name="recompress">
      <param name="file">*.tar</param>
      <param name="compression">gz</param>
   <service name="extract_file">
      <param name="archive">*.tar.gz</param>
      <param name="files">*/*.dsc</param>

The .dsc file is modified from the output from a proper debuild -us -uc locally. I learned the trick of zeroing out the checksums from another OBS project, waterfox-classic-kpe.

Format: 3.0 (quilt)
Source: fluxbox-themes-stackrpms
Binary: fluxbox-themes-stackrpms
Architecture: all
Version: 0.0.1-1+devuan
Maintainer: Ben Stack 
Standards-Version: 4.1.4
Build-Depends: debhelper (>= 12~)
 fluxbox-themes-stackrpms deb x11 optional arch=all
 00000000000000000000000000000000 1 fluxbox-themes-stackrpms_0.0.1.orig.tar.gz
 00000000000000000000000000000000 1 fluxbox-themes-stackrpms_0.0.1-1+devuan.debian.tar.xz

And since the filenames do not match, I think that section is not even necessary. Thankfully OBS figures out what tarballs to use where for the debuild. To quote my original distro (Korora): “Standing on the shoulders of giants.”


I can control my osc package with only one file in the obs source control: the _service file! This reduces the need to pass multiple assets, which can be large.


Internet searches

How I actually found the main weblink open build service services recompress


Shell: find duplicate files and replace most with symlinks

This is probably not the best solution, but it’s the one I wanted to work with. I intended to reduce disk space of 3 similar projects. Here is my shell script to find duplicate files (by md5sum) and replace any secondary file with a symlink to the relative path of the original file.

#!/usr/bin/env sh
# File:
# License: CC-BY-SA 3.0
# Author: bgstack15
# Startdate: 2020-02-07 14:03
# Title: Script that Replaces Duplicate Files with Symlinks
# Purpose:
# History:
# Usage:
# Reference:
# Improve:
# Dependencies:
#    coreutils >= 8.23


#results="$( find "${INDIR}" ! -type d ! -type l -exec md5sum {} + | sort )"
find "${INDIR}" ! -type d ! -type l -exec md5sum {} + | sort | \
   awk '{a[$1]=a[$1]":"$2} END {for (i in a){print a[i]}}' | sed -r -e 's/^://;' | \
   while IFS=':' read main child1 child2 child3 child4 ;
      while test $x -lt 4 ;
         x=$(( x + 1 ))
         eval thischild="\${child$x}"
         if test -n "${thischild}" ;
            linkname="$( realpath --relative-to "$( dirname "${thischild}" )" "${main}" 2>/dev/null )"
            test -n "${DEBUG}" && echo "ln -sf ${linkname} ${thischild}" 1>&2
            test -z "${DRYRUN}" && ln -sf "${linkname}" "${thischild}"

I was going to do this task for myself by hand, but then a quick investigation showed 73 files that were duplicates. Because of the small size of the project, I decided to just run it in shell and not revert to Python. I don’t need efficiency; I just need to run it once, really.

The tricky bits are in the very front of the logic. The awk associate array builds a list of all filenames that correspond with an md5sum. Then, stripping out the leading colon (separator), I pipe the output to a while for easy variable naming. And then loop a few times (hard-coded to 4) and if item number X exists, get the relative path to the main file, and force create the symlink.

Python3: convert relative date to UTC timestamp

I needed to convert a relative time string, e.g., “Yesterday at 3:08 am,” to a UTC timestamp. Apparently this is not that easy to find on the Internet.
My research combines at least 3 different sources to accomplish my goal.

My incoming timezone is UTC-0500 (“US/Eastern”). And I realize it is unfortunate that I am doing some string-to-ints and back and forth. Well, please share how you could do better!


#!/usr/bin/env python3
from datetime import timedelta
from parsedatetime import Calendar
from pytz import timezone 

indate = "Yesterday at 3:08 am"

# long version, for explanation
cal = Calendar()
dto, _ = cal.parseDT(datetimeString=indate, tzinfo=timezone("US/Eastern"))
add_hours = int((str(dto)[-6:])[:3])
outdate = (timedelta(hours=-add_hours) + dto).strftime('%Y-%m-%dT%H:%M:%SZ')

# short form
dto, _ = Calendar().parseDT(datetimeString=indate, tzinfo=timezone("US/Eastern"))
outdate = (timedelta(hours=-int((str(dto)[-6:])[:3])) + dto).strftime('%Y-%m-%dT%H:%M:%SZ')

Sample run

$ ./ 
indate: Yesterday at 3:08 am
outdate: 2020-02-02T08:08:00Z
outdate: 2020-02-02T08:08:00Z



  1. python – Convert relative date string to absolute date – Stack Overflow
  2. bear/parsedatetime: Parse human-readable date/time strings []
  3. python – Convert UTC datetime string to local datetime – Stack Overflow

Package for devuan: waterfox

In my Internet searching I have not found a Devuan-centric Waterfox classic package. So I decided to bundle it myself!
You can see the build info for it in OBS, as well as use the nice download page to get it for yourself. This build is for Devuan Ceres.

It excludes pulseaudio and dbus, which is why I call it “Devuan centric.” While the Open Build Service only offers Debian builds, the package will install just fine on Devuan ceres. If you are the more paranoid type, you can examine my sources and even build the package yourself on Devuan!


I follow a number of people on the Internet to learn packing tricks for dpkgs, or for how to build Waterfox. I know basically nothing about real software development, but I can build scripts pretty well, especially when I can follow good examples.

Install build dependencies from source files, dpkg and rpm

For a dpkg

cd $packagedir ;
sudo apt-get install ./${package}-build-deps*.deb

Source: ubuntu – Given a debian source package – How do I install the build-deps? – Server Fault

For an rpm

yum-builddep my-package.spec


dnf builddep my-package.spec

fedora – Automatically install build dependencies prior to building an RPM package – Stack Overflow/13228992#13228992

Build rpm with Jenkins project

Here is how I build rpm files in Jenkins.


Add a Fedora node to the cluster. I am running Jenkins on Devuan, which obviously is not ideal for building rpms.
I added a few labels, which are space-delimited which I found unusual. But whatever. I used a service account, and set up an ssh key for passwordless authentication.
Configuring a Jenkins node, with name fc30x-01a and a remote directory and ssh setup.
Add a user and grant them some specific sudo permissions:

useradd jenkins

cat <<EOF >/etc/sudoers.d/70_jenkins
User_Alias JENKINS = jenkins
Defaults:JENKINS !requiretty
JENKINS fc30x-01a=(root)	NOPASSWD: /usr/bin/dnf -y builddep *

Install some build tools:

sudo dnf -y install rpm-build rpmdevtools

My rpmbuild workflow in Jenkins

Add a new project. Restrict it to run on label “fedora.”
Project configuration showing restrict where project is run, to label "fedora"
Like last time, I am checking out my git repo to a local subdirectory.
Screenshot of project configuration showing SCM, and additional behavior of "Check out to a sub-directory"
All the build steps for this project are shell commands.
The first command uses some tooling I learned about for this project: spectool. The dnf installs the build dependencies, and spectool downloads all the source files that are not already present in the directory.

pwd ; ls -altr ; mkdir -p rpmbuild ; cd rpmbuild ;
cp -p ../work/veracrypt/* . || :;
sudo dnf -y builddep *.spec ;
spectool -g *.spec ;

The actual build command happens in the second step. I am using a few macro definitions to keep everything happening in the present working directory.

cd rpmbuild ; rpmbuild --define "_topdir %(pwd)" --define "_builddir %{_topdir}" --define "_rpmdir %{_topdir}" --define "_sourcedir %{_topdir}" --define "_srcrpmdir %{_topdir}" --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" -ba *.spec

And the final step deploys the files to my nfs share for manual curation.

mkdir -p /mnt/public/Public/${JOB_NAME} ;
cp -p *.rpm */*.rpm /mnt/public/Public/${JOB_NAME}/ || :



  1. fedora – Automatically install build dependencies prior to building an RPM package – Stack Overflow
  2. rpm – How do I get rpmbuild to download all of the sources for a particular .spec? – Stack Overflow
  3. Rpmdevtools – Fedora Project Wiki
  4. Build RPMs using Jenkins/Hudson