devops

    I’m using Ansible to manage a small fleet of Raspberry Pis. I’d been using the copy module to set a value in /sys:

    - name: Enable compressed swap now (with zsmalloc)
      become: true
      ansible.builtin.copy:
        content: zsmalloc
        dest: /sys/module/zswap/parameters/zpool
        unsafe_writes: true
    

    But that always reports that the file changed, even if it already had that value. Today I got the lineinfile module to update the value. This only says the value changed when it actually did:

    - name: Enable compressed swap now (with zsmalloc)
      become: true
      ansible.builtin.lineinfile:
        path: /sys/module/zswap/parameters/zpool
        regexp: "^(?!zsmalloc).*$"
        line: zsmalloc
        unsafe_writes: true
    

    Since these “files” only have one line, this uses regexp to see if that line already matches the expected value. If so, it moves on. If not, it writes the new value.

    Note: unsafe_writes: true is there because you can’t write arbitrary filenames into /sys and then mv them into place. You have to write directly to the target “file”.

    I’ve spent too much of this weekend writing Ansible to make all my Raspberry Pis similar.

    This might say more than I’d wish about my nerd level, and about how many tiny computers I have laying around.

    I’ve been using Just for a while as a task runner. It’s similar to Make, but optimized for developer ergonomics with a vastly simpler syntax and a wonderful CLI. I’d also been using Mise for other environment management things, such as installing specific versions of Python and NPM and other tools in a project directory.

    Someone introduced me to Mise’s own newish task runner, and it just might win me over from Just for most things:

    1. Instead of using 2 tools, I can use 1.
    2. Just still feels nicer to me, perhaps because I’m more used to it, but Mise is good enough that I don’t think I’d miss the extra features.
    3. Mise lets you write tasks in separate files, which lets any editor handle them well without having to support justfile syntax, but still shares a CLI with inline tasks.

    I like it.

    Forgejo Runner in rootless Podman on Debian

    I wanted to experiment with Forgejo’s Actions as a DIY alternative to GitHub Actions, using a nearby Raspberry Pi as a build server. I also wanted to deviate slightly from their Runner installation process by executing the Runner and rootless Podman as a regular, non-privileged user and without using the system-level systemctl. It was pretty easy once I wrapped my head around it.

    1. Set up the runner user. Since I was using Podman, not Docker, I didn’t have to add it to the docker group. As root:
    root# useradd --create-home forgejo-runner
    

    This created user number 1001 on my system. Remember that number later when it’s time to configure systemd.

    1. Allow that user to run commands via systemctl without logging in and launching them manually:
    root# loginctl enable-linger forgejo-runner
    
    1. Use machinectl instead of su to become the forgejo-runner user. Without this, most systemd commands will fail with the Failed to connect to bus: No medium found message. I’m certain there’s a way to get su or sudo to play nicely with dbus but I had more interesting problems to solve today than this.
    root# apt install systemd-container
    root# machinectl shell forgejo-runner@
    
    1. Run podman-system-service as the forgejo-runner user:
    $ systemctl --user enable podman.socket
    $ systemctl --user start podman.socket
    
    1. Run the forgejo-runner program as the forgejo-runner user. I lightly modified the standard forgejo-runner.service file:
    $ cat > .config/systemd/user/forgejo-runner.service <<EOHD
    [Unit]
    Description=Forgejo Runner
    Documentation=https://forgejo.org/docs/latest/admin/actions/
    After=podman.socket
    
    [Service]
    ExecStart=/usr/local/bin/forgejo-runner daemon
    ExecReload=/bin/kill -s HUP $MAINPID
    # 1001 is the forgejo-runner user's UID
    Environment="DOCKER_HOST=unix:///run/user/1001/podman/podman.sock"
    
    # This user and working directory must already exist
    WorkingDirectory=/home/forgejo-runner
    Restart=on-failure
    TimeoutSec=0
    RestartSec=10
    
    [Install]
    WantedBy=default.target
    EOHD
    $ systemctl --user daemon-reload
    $ systemctl --user enable forgejo-runner.service
    $ systemctl --user start forgejo-runner.service
    

    I rebooted my RPi to make sure it would start on its own and it did. Yay! Now I can run Forgejo Actions on my little server and everything works as documented.