build-packaging-release
Modify build scripts, packaging configuration, or CI/CD release workflows for open-bc with full support for Windows (MSI via WiX), Linux .deb (Ubuntu/Debian), Linux Flatpak, and Linux .rpm (Fedora/RHE
SKILL.md.claude/skills/build-packaging-release/SKILL.md
GitHubBuild, Packaging & Release Skill for OpenBC
Platform Support Matrix
OpenBC produces four artifact types across two platforms:
| Platform | Package | Tool chain | Runner |
|---|---|---|---|
| Linux (Ubuntu/Debian) | .deb |
cmake --build + CPack DEB |
ubuntu-24.04 |
| Linux (all) | .flatpak |
flatpak-builder (KDE/Qt runtime) |
ubuntu-24.04 |
| Linux (Fedora/RHEL) | .rpm |
cmake --build + CPack RPM |
Fedora container on ubuntu-24.04 |
| Windows | .msi |
MSYS2 UCRT64 + WiX 3 | windows-2022 |
Every build or release change MUST be reviewed for all four targets.
Repository Build Layout
open-bc/
scripts/
build.sh # Main orchestrator: -c (clean), -p (win|lin), -s (deb|rpm|flatpak)
packaging/
io.github.devopsnextgenx.open-bc.yml # Flatpak manifest (KDE/Qt6 runtime)
CMakeLists.txt # Version, CPack, Qt/Rust integration
Cargo.toml # Workspace root
Cargo.lock
.github/
workflows/
release.yml # GitHub Actions: linux (deb+flatpak), rpm, msi, release jobs
xtask/ # Rust build driver (cargo xtask)
Version Bump Protocol
The version is declared ONCE in CMakeLists.txt:
set(CPACK_PACKAGE_VERSION "1.0.3")
ALWAYS update this first. The release workflow validates that the pushed tag matches:
v="$(tr -d '\r' < CMakeLists.txt | sed -n 's/^set(CPACK_PACKAGE_VERSION "\(.*\)")/\1/p' | head -1)"
[ "v$v" = "${GITHUB_REF_NAME}" ] || { echo "tag mismatch"; exit 1; }
Steps for a version bump:
- Update
set(CPACK_PACKAGE_VERSION "X.Y.Z")inCMakeLists.txt - Verify
build.shdoes not hard-code the version anywhere (it should read from CMake) - Commit, then
git tag vX.Y.Z && git push origin vX.Y.Z
scripts/build.sh -- Key Flags
# Full Windows MSI build:
./scripts/build.sh -c -p win
# Linux .deb (clean build):
./scripts/build.sh -c -p lin -s deb
# Linux Flatpak (no clean -- reuses host build artifacts for speed):
./scripts/build.sh -p lin -s flatpak
# Linux .rpm:
./scripts/build.sh -c -p lin -s rpm
When modifying build.sh:
- Windows path convention: always use
/c/msys64/ucrt64/bin-- NOTC:\msys64- MSYS2 Git Bash maps Windows drives:
C:->/c - This is hardcoded in
build.shand referenced inCLAUDE.md
- MSYS2 Git Bash maps Windows drives:
- Required env for Windows dev builds:
export PATH="/c/msys64/ucrt64/bin:/c/msys64/usr/bin:$PATH" export OPENSSL_DIR=/c/msys64/ucrt64 - Flatpak build is sandboxed -- it does NOT reuse the host's Rust build cache
- The
debstep runs BEFOREflatpakin CI so the.debartifact stays inbuild/lin/packages/
GitHub Actions -- release.yml Structure
Job: linux (ubuntu-24.04)
Builds .deb and .flatpak:
- name: Free disk space
run: sudo rm -rf /usr/share/dotnet /opt/ghc ... # GH runners have limited space
- name: Install build + flatpak dependencies
run: |
sudo apt-get install -y \
build-essential cmake ninja-build pkg-config perl \
qt6-base-dev qt6-declarative-dev qt6-declarative-dev-tools qt6-multimedia-dev \
libqt6opengl6-dev libgl1-mesa-dev \
libssl-dev zlib1g-dev liblzma-dev \
flatpak flatpak-builder appstream
- name: Allow user namespaces # Required for flatpak-builder sandbox on Ubuntu 24.04
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Build .deb
run: ./scripts/build.sh -c -p lin -s deb
- name: Build Flatpak
run: ./scripts/build.sh -p lin -s flatpak # no -c: reuses host build
When adding a new Linux dependency (apt package):
- Add to the
apt-get installlist in thelinuxjob - Check if the package is also needed in the RPM job (Fedora package name will differ)
Job: rpm (Fedora container on ubuntu-24.04)
rpm:
runs-on: ubuntu-24.04
container: fedora:latest
steps:
- name: Install build dependencies
run: |
dnf install -y git gcc-c++ make cmake ninja-build rust cargo rpm-build which perl-core \
qt6-qtbase-devel qt6-qtdeclarative-devel qt6-qtmultimedia-devel \
mesa-libGL-devel libxkbcommon-devel \
openssl-devel zlib-devel xz-devel
- run: ./scripts/build.sh -c -p lin -s rpm
Fedora package naming differences from Ubuntu:
| Ubuntu | Fedora |
|---|---|
qt6-base-dev |
qt6-qtbase-devel |
qt6-multimedia-dev |
qt6-qtmultimedia-devel |
libssl-dev |
openssl-devel |
zlib1g-dev |
zlib-devel |
liblzma-dev |
xz-devel |
libgl1-mesa-dev |
mesa-libGL-devel |
pkg-config |
pkgconf |
Job: msi (windows-2022, MSYS2)
msi:
runs-on: windows-2022
steps:
- uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
release: false # reuse preinstalled C:\msys64
update: true
install: >-
git make
mingw-w64-ucrt-x86_64-toolchain
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-ninja
mingw-w64-ucrt-x86_64-rust
mingw-w64-ucrt-x86_64-qt6-base
mingw-w64-ucrt-x86_64-qt6-declarative
mingw-w64-ucrt-x86_64-qt6-tools
mingw-w64-ucrt-x86_64-qt6-multimedia
mingw-w64-ucrt-x86_64-openssl
mingw-w64-ucrt-x86_64-zlib
mingw-w64-ucrt-x86_64-xz
mingw-w64-ucrt-x86_64-pkgconf
- name: Install WiX 3.14 (at C:\msys64\opt\wix -- build.sh looks here)
shell: pwsh
run: |
Invoke-WebRequest https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip -OutFile wix.zip
Expand-Archive wix.zip -DestinationPath C:\msys64\opt\wix
- name: Build MSI
shell: msys2 {0}
run: |
./scripts/build.sh -c -p win
v="$(tr -d '\r' < CMakeLists.txt | sed -n 's/^set(CPACK_PACKAGE_VERSION "\(.*\)")/\1/p' | head -1)"
mv build/win/packages/openbc-installer.msi "build/win/packages/openbc-${v}-windows-x64.msi"
When adding a new MSYS2 package:
- All MSYS2 packages for UCRT64 are prefixed
mingw-w64-ucrt-x86_64- - Search at https://packages.msys2.org/ for the UCRT64 package name
- Add to the
install:block in themsys2/setup-msys2@v2step
Job: release (tags only)
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [linux, rpm, msi]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: pkg-*
path: dist
merge-multiple: true
- name: Checksums
run: cd dist && sha256sum * > SHA256SUMS && cat SHA256SUMS
- uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true
CMakeLists.txt -- Key Packaging Sections
# Version (single source of truth):
set(CPACK_PACKAGE_VERSION "X.Y.Z")
# CPack DEB config:
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "...")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6core6, libqt6multimedia6, ...")
# CPack RPM config:
set(CPACK_RPM_PACKAGE_REQUIRES "qt6-qtbase, qt6-qtmultimedia, ...")
# Desktop integration:
install(FILES resources/io.github.devopsnextgenx.open-bc.desktop DESTINATION share/applications)
install(FILES resources/io.github.devopsnextgenx.open-bc.metainfo.xml DESTINATION share/metainfo)
install(FILES resources/icons/io.github.devopsnextgenx.open-bc.png DESTINATION share/icons/hicolor/256x256/apps)
When adding a new runtime library dependency:
- Add to
CPACK_DEBIAN_PACKAGE_DEPENDSfor.deb - Add to
CPACK_RPM_PACKAGE_REQUIRESfor.rpm - Add to Flatpak manifest
finish-argsormodulesif it is not in the KDE/Qt6 runtime
Flatpak Manifest -- scripts/packaging/io.github.devopsnextgenx.open-bc.yml
Key conventions:
- Base runtime:
org.kde.Platform+ matching SDK (track the Qt6 version used) - App ID must match:
io.github.devopsnextgenx.open-bc(also must match.desktopfileStartupWMClassandQGuiApplication::setDesktopFileName) finish-argsmust include display/audio/filesystem permissions for features used- New native libraries should be added as
modulesentries; prefer shared-modules where possible
Platform-Specific Code in the Codebase
All OS-specific C++ code must be guarded:
#ifdef Q_OS_WIN
// Windows-only: Media Foundation backend, console attachment, etc.
qputenv("QT_MEDIA_BACKEND", "windows");
#endif
#ifdef Q_OS_LINUX
// Linux-only code here
#endif
From AGENTS.md -- file path and user conventions:
// CORRECT:
const QString username = qEnvironmentVariable("USER",
qEnvironmentVariable("USERNAME", "unknown")); // USERNAME on Windows
QString logPath = QDir::homePath() + "/.config/open-bc/...";
// WRONG:
const char* home = "/home/user"; // never hard-code
Console output (Windows GUI-subsystem binary):
// Use printToConsole() for --help, --version, errors before Qt starts
// It attaches to the parent console on Windows (AttachConsole API)
printToConsole("message\n"); // NOT printf, NOT std::cout
Checklist for Every Build/Packaging Change
Always verify all four targets:
Windows MSI:
- MSYS2 UCRT64 package list updated if new native dependency added
- WiX
.wxsfile updated if new files/directories installed -
build.shWindows path conventions preserved (/c/msys64/...) - No POSIX-only shell syntax (test in Git Bash via MSYS2 shell)
Linux .deb:
-
CPACK_DEBIAN_PACKAGE_DEPENDSupdated for new runtime deps -
apt-get installlist inlinuxjob updated for new build-time deps - Desktop/AppStream metadata up to date
Linux .rpm:
-
CPACK_RPM_PACKAGE_REQUIRESupdated for new runtime deps -
dnf installlist inrpmjob updated (Fedora package naming -- see table above) - Fedora container version (
fedora:latest) still provides needed Qt6 version
Linux Flatpak:
- New libraries added as Flatpak modules or available in the base runtime
-
finish-argsupdated if new system permissions needed (audio, network, etc.) - User namespace sandbox sysctl step still present in
linuxjob -
flatpak-builder--share/--socketpermissions documented
General CI hygiene:
-
cargo check --workspacepasses on all platforms -
releasejobneeds:list includes all build jobs - Artifact upload names (
name: pkg-<format>) matchdownload-artifactpatternpkg-* -
SHA256SUMSstill covers all produced files -
generate_release_notes: truepreserved insoftprops/action-gh-release - Disk space
Free disk spacestep still removes large unused toolchains on Linux runner -
Allow user namespacessysctl step present on ubuntu-24.04 Flatpak job
Adding a New Package Format
To add a new Linux package format (e.g. AppImage):
- Add a new flag to
build.sh(e.g.-s appimage) - Add a new step (or new job) in
.github/workflows/release.yml - Upload artifact with a unique name (e.g.
name: pkg-appimage) - Ensure the artifact name matches the
pattern: pkg-*download in thereleasejob - Add build dependencies to the appropriate job's install step
- Document in this skill
Common Mistakes to Avoid
| Mistake | Correct approach |
|---|---|
Hard-coding version in build.sh |
Read from CMakeLists.txt via sed (already done) |
Using apt package names in the Fedora job |
Use dnf + Fedora package names (see table) |
Using Windows backslash paths in build.sh |
MSYS2 shell uses POSIX paths (/c/msys64/...) |
| Adding a dep to Linux job but not RPM job | All three Linux targets need build+runtime deps checked |
Forgetting release: false in msys2 action |
Saves ~10 min; preinstalled toolchain is sufficient |
| Skipping disk space cleanup on Linux | GH ubuntu-24.04 runners may fail with out-of-space errors |
Using softprops/action-gh-release@v1 |
Use @v2 (v1 deprecated) |
Forgetting to update CPACK_*DEPENDS in CMakeLists.txt |
Package installs fine but runtime breaks on user machines |
Adding a release job dependency without the job existing |
Workflow fails at queue time |