┌────────────────────────────┐
│   Continuous Void Linux    │
│    Packaging: Automated    │
│  xbps-src Upstream PR Bot  │
│ 2026-09-04                 │
│                            │
├────────────────────────────┤
│ << Back to Blog            │
└────────────────────────────┘
╔══════════════════════════════════════╗
║   Continuous Void Linux Packaging:   ║
║  Automated xbps-src Upstream PR Bot  ║
║ 2026-09-04                           ║
║                                      ║
╠══════════════════════════════════════╣
║ << Back to Blog                      ║
╚══════════════════════════════════════╝
╔══════════════════════════════════════════════════════════╗
║   Continuous Void Linux Packaging: Automated xbps-src    ║
║                     Upstream PR Bot                      ║
║ 2026-09-04                                               ║
║                                                          ║
╠══════════════════════════════════════════════════════════╣
║ << Back to Blog                                          ║
╚══════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║     Continuous Void Linux Packaging: Automated xbps-src Upstream PR Bot      ║
║ 2026-09-04                                                                   ║
║                                                                              ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ << Back to Blog                                                              ║
╚══════════════════════════════════════════════════════════════════════════════╝

Continuous Void Linux Packaging: Automated xbps-src Upstream PR Bot

Table of Contents

  1. The Context / The Problem
  2. The Deep-Dive / Root Cause Analysis
  3. The Implementation / Architecture
  4. Lessons Learned & Best Practices
  5. References

The Context / The Problem

Maintaining custom software packages across production servers running Void Linux requires navigating xbps-src, the distribution's source-based package build system. In our infrastructure, edge routing daemons, specialized DNS exporters, and custom crypto payment gateways run natively on Void bare metal to minimize glibc overhead and eliminate systemd bloat.

Historically, keeping our internal package templates (template files inside void-packages/srcpkgs/) up to date with new software releases was an entirely manual, error-prone process. A developer had to download upstream release tarballs, compute SHA-256 checksums, increment version strings, test local compilation across x86_64 and aarch64 architectures, and commit changes.

When upstream dependencies issued emergency security patches, package lag left edge servers exposed. We needed an automated CI/CD pipeline capable of polling upstream GitHub releases, bumping package templates, verifying clean cross-architecture builds inside chroot containers, and opening clean upstream pull requests.


The Deep-Dive / Root Cause Analysis

Building continuous packaging automation for Void Linux revealed three unique distribution challenges:

1. The Strict Template Lint Contract

Void Linux maintains exceptionally rigorous linter checks (xlint). Common automated update tools fail upstream review by introducing trailing whitespace, mismatched variable assignments, or unquoted revision increments.

2. Multi-Architecture Cross-Compilation

Packages that build cleanly on standard x86_64 frequently fail when compiled for x86_64-musl or cross-compiled for aarch64. Relying on single-host testing produces broken binary packages that crash on ARM edge routers.


The Implementation / Architecture

We engineered an automated packaging bot driven by GitHub Actions and a custom Python CLI (xbps-bump).

1. Automated Template Updater Script

The core update script queries upstream Git tags, verifies cryptographic signatures, computes release tarball hashes, and modifies the srcpkgs/<pkg>/template file:

#!/usr/bin/env python3
import sys, re, hashlib, urllib.request

def bump_template(pkg_name: str, new_version: str, tarball_url: str):
    template_path = f"srcpkgs/{pkg_name}/template"
    with open(template_path, "r") as f:
        content = f.read()

    # Download release tarball and compute sha256
    req = urllib.request.Request(tarball_url, headers={"User-Agent": "VoidPackagingBot/1.0"})
    with urllib.request.urlopen(req) as resp:
        tarball_bytes = resp.read()
    new_sha256 = hashlib.sha256(tarball_bytes).hexdigest()

    # Update version, reset revision to 1, and update checksum
    content = re.sub(r'version=.*', f'version={new_version}', content)
    content = re.sub(r'revision=.*', 'revision=1', content)
    content = re.sub(r'checksum=.*', f'checksum={new_sha256}', content)

    with open(template_path, "w") as f:
        f.write(content)
    print(f"Successfully bumped {pkg_name} to {new_version} (checksum: {new_sha256[:12]}...)")

if __name__ == "__main__":
    bump_template(sys.argv[1], sys.argv[2], sys.argv[3])

2. Multi-Target Matrix CI Pipeline

The GitHub Actions workflow executes sandboxed container builds across both glibc and musl runtimes before creating pull requests:

name: Continuous XBPS Packaging
on:
  schedule:
    - cron: '0 4 * * *' # Daily check at 04:00 UTC
  workflow_dispatch:

jobs:
  build-matrix:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target: [x86_64, x86_64-musl, aarch64]
    steps:
      - name: Checkout void-packages
        uses: actions/checkout@v4

      - name: Setup xbps-src chroot
        run: |
          ./xbps-src binary-bootstrap
          ./xbps-src -a ${{ matrix.target }} pkg ${{ env.PACKAGE_NAME }}

      - name: Run xlint validation
        run: xlint srcpkgs/${{ env.PACKAGE_NAME }}/template

Lessons Learned & Best Practices

  1. Always Reset Revision to 1 on Version Bumps: In xbps-src, failing to reset revision=1 when incrementing version causes downstream package database update collisions.
  2. Test Musl Runtimes in Parallel: Many C/Rust projects make subtle glibc assumptions regarding thread stack sizes or locale handling. Building against x86_64-musl in CI caught dozens of subtle edge-case crashes.
  3. Automate Changelog Extraction in PR Bodies: Extracting upstream release notes directly into PR descriptions cut maintainer review time by 60%, resulting in faster merges.

References