Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
364 changes: 364 additions & 0 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,364 @@
name: Build and Release

on:
push:
branches:
- master
- ubuntu25.10-wayland
tags:
- "v*"
pull_request:
branches:
- master
workflow_dispatch:

permissions:
contents: write
Comment on lines +15 to +16
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

permissions: contents: write is granted for the entire workflow, including the build job that runs on pull_request. Even though fork PRs get a read-only token, same-repo PRs would run with a write-capable token, which is broader than needed for building/testing. Consider setting default workflow permissions to read-only and granting contents: write only on the release job (or even just the specific step) that uploads the GitHub Release.

Copilot uses AI. Check for mistakes.

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: Linux
os: ubuntu-24.04
artifact_name: dsview-linux-x86_64
archive_name: linux-x86_64
- name: Windows
os: windows-2022
artifact_name: dsview-windows-x86_64
archive_name: windows-x86_64
- name: macOS
os: macos-latest
artifact_name: dsview-macos
archive_name: macos

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Compute build metadata
id: meta
shell: bash
run: |
python3 <<'PY' >> "${GITHUB_OUTPUT}"
import os
import pathlib
import re

cmake = pathlib.Path("CMakeLists.txt").read_text(encoding="utf-8")

def pick(name: str) -> str:
match = re.search(rf"set\({name}\s+([0-9]+)\)", cmake)
if not match:
raise SystemExit(f"Unable to find {name} in CMakeLists.txt")
return match.group(1)

project_version = ".".join(
[
pick("DS_VERSION_MAJOR"),
pick("DS_VERSION_MINOR"),
pick("DS_VERSION_MICRO"),
]
)

ref_type = os.environ.get("GITHUB_REF_TYPE", "")
ref_name = os.environ.get("GITHUB_REF_NAME", "")
short_sha = os.environ.get("GITHUB_SHA", "")[:7]

if ref_type == "tag":
tag_version = ref_name[1:] if ref_name.startswith("v") else ref_name
if tag_version != project_version:
raise SystemExit(
f"Git tag {ref_name} does not match project version {project_version}"
)
artifact_version = project_version
package_version = project_version
release_title = f"DSView {project_version}"
else:
artifact_version = f"{project_version}+git.{short_sha}"
package_version = f"{project_version}+git{short_sha}"
release_title = f"DSView {artifact_version}"

print(f"project_version={project_version}")
print(f"artifact_version={artifact_version}")
print(f"package_version={package_version}")
print(f"release_title={release_title}")
PY

- name: Install Linux dependencies
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y \
cmake ninja-build pkg-config \
qt6-base-dev qt6-tools-dev qt6-wayland \
libglib2.0-dev libfftw3-dev libusb-1.0-0-dev \
libboost-dev zlib1g-dev python3-dev

- name: Set up MSYS2
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: >-
base-devel
git
zip
mingw-w64-x86_64-toolchain
mingw-w64-x86_64-cmake
mingw-w64-x86_64-ninja
mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-python
mingw-w64-x86_64-zlib
mingw-w64-x86_64-glib2
mingw-w64-x86_64-libusb
mingw-w64-x86_64-fftw
mingw-w64-x86_64-boost
mingw-w64-x86_64-qt6-base
mingw-w64-x86_64-qt6-tools

- name: Install macOS dependencies
if: runner.os == 'macOS'
shell: bash
run: |
brew update
brew install cmake ninja pkg-config qt@6 glib libusb fftw boost python

- name: Configure Linux
if: runner.os == 'Linux'
shell: bash
run: cmake -S . -B build -G Ninja -DDSVIEW_PREFER_QT6=ON -DDSVIEW_PACKAGE_VERSION_OVERRIDE="${{ steps.meta.outputs.package_version }}"

- name: Configure Windows
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
set -euo pipefail
export PATH="/mingw64/bin:${PATH}"
export PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:/mingw64/share/pkgconfig"
export CMAKE_PREFIX_PATH="/mingw64"
export Python3_ROOT_DIR="/mingw64"
export Boost_ROOT="/mingw64"
cmake_bin="$(command -v cmake)"
pkg_config_bin="$(command -v pkg-config || command -v pkgconf)"
python_bin="$(command -v python3 || command -v python)"
python_include="$("${python_bin}" -c 'import sysconfig; print(sysconfig.get_path("include"))')"
python_library="$("${python_bin}" -c 'import pathlib, sysconfig; libdir = sysconfig.get_config_var("LIBDIR") or sysconfig.get_config_var("LIBPL") or ""; library = sysconfig.get_config_var("LDLIBRARY") or sysconfig.get_config_var("LIBRARY") or ""; print(pathlib.Path(libdir, library) if libdir and library else "")')"
echo "cmake=${cmake_bin}"
echo "pkg-config=${pkg_config_bin}"
echo "python=${python_bin}"
echo "python-include=${python_include}"
echo "python-library=${python_library}"
"${cmake_bin}" -S . -B build -G Ninja \
-DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}" \
-DPKG_CONFIG_EXECUTABLE="${pkg_config_bin}" \
-DPython3_EXECUTABLE="${python_bin}" \
-DPython3_ROOT_DIR="${Python3_ROOT_DIR}" \
-DPython3_INCLUDE_DIR="${python_include}" \
-DPython3_LIBRARY="${python_library}" \
-DBoost_ROOT="${Boost_ROOT}" \
-DBOOST_INCLUDEDIR="/mingw64/include" \
-DBOOST_LIBRARYDIR="/mingw64/lib" \
-DDSVIEW_PREFER_QT6=ON \
-DDSVIEW_PACKAGE_VERSION_OVERRIDE="${{ steps.meta.outputs.package_version }}" \
2>&1 | tee configure-windows.log
status=${PIPESTATUS[0]}
if [[ ${status} -ne 0 ]]; then
test -f build/CMakeFiles/CMakeError.log && cat build/CMakeFiles/CMakeError.log
test -f build/CMakeFiles/CMakeOutput.log && cat build/CMakeFiles/CMakeOutput.log
exit ${status}
fi

- name: Configure macOS
if: runner.os == 'macOS'
shell: bash
run: |
brew_prefix="$(brew --prefix)"
qt_prefix="$(brew --prefix qt@6)"
glib_prefix="$(brew --prefix glib)"
libusb_prefix="$(brew --prefix libusb)"
fftw_prefix="$(brew --prefix fftw)"
boost_prefix="$(brew --prefix boost)"
export PATH="${qt_prefix}/bin:${brew_prefix}/bin:$PATH"
pkg_config_bin="$(command -v pkg-config || command -v pkgconf)"
python_bin="$(command -v python3 || command -v python)"
python_prefix="$(cd "$(dirname "${python_bin}")/.." && pwd)"
export PKG_CONFIG_PATH="${glib_prefix}/lib/pkgconfig:${libusb_prefix}/lib/pkgconfig:${fftw_prefix}/lib/pkgconfig:${boost_prefix}/lib/pkgconfig:${python_prefix}/lib/pkgconfig:${brew_prefix}/lib/pkgconfig:${qt_prefix}/lib/pkgconfig"
python_include="$("${python_bin}" -c 'import sysconfig; print(sysconfig.get_path("include"))')"
python_library="$("${python_bin}" -c 'import pathlib, sysconfig; libdir = sysconfig.get_config_var("LIBDIR") or sysconfig.get_config_var("LIBPL") or ""; library = sysconfig.get_config_var("LDLIBRARY") or sysconfig.get_config_var("LIBRARY") or ""; print(pathlib.Path(libdir, library) if libdir and library else "")')"
cmake -S . -B build -G Ninja \
-DDSVIEW_PREFER_QT6=ON \
-DDSVIEW_PACKAGE_VERSION_OVERRIDE="${{ steps.meta.outputs.package_version }}" \
-DCMAKE_PREFIX_PATH="${qt_prefix};${glib_prefix};${libusb_prefix};${fftw_prefix};${boost_prefix};${python_prefix};${brew_prefix}" \
-DPKG_CONFIG_EXECUTABLE="${pkg_config_bin}" \
-DPython3_EXECUTABLE="${python_bin}" \
-DPython3_ROOT_DIR="${python_prefix}" \
-DPython3_INCLUDE_DIR="${python_include}" \
-DPython3_LIBRARY="${python_library}" \
-DBoost_ROOT="${boost_prefix}" \
2>&1 | tee configure-macos.log
status=${PIPESTATUS[0]}
if [[ ${status} -ne 0 ]]; then
test -f build/CMakeFiles/CMakeError.log && cat build/CMakeFiles/CMakeError.log
test -f build/CMakeFiles/CMakeOutput.log && cat build/CMakeFiles/CMakeOutput.log
exit ${status}
fi

- name: Build Linux
if: runner.os == 'Linux'
shell: bash
run: cmake --build build --parallel

- name: Build Windows
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
cmake --build build --parallel 2>&1 | tee build-windows.log
status=${PIPESTATUS[0]}
if [[ ${status} -ne 0 ]]; then
exit ${status}
fi

- name: Build macOS
if: runner.os == 'macOS'
shell: bash
run: |
cmake --build build --parallel 2>&1 | tee build-macos.log
status=${PIPESTATUS[0]}
if [[ ${status} -ne 0 ]]; then
exit ${status}
fi

- name: Upload failure diagnostics
if: failure()
uses: actions/upload-artifact@v5
with:
name: failure-diagnostics-${{ matrix.artifact_name }}-${{ steps.meta.outputs.artifact_version }}
path: |
configure-*.log
build-*.log
build/CMakeFiles/CMakeError.log
build/CMakeFiles/CMakeOutput.log
if-no-files-found: ignore
retention-days: 14

- name: Smoke check Linux
if: runner.os == 'Linux'
shell: bash
run: ./build.dir/DSView -v

- name: Smoke check Windows
if: runner.os == 'Windows'
shell: msys2 {0}
run: ./build.dir/DSView.exe -v

- name: Smoke check macOS
if: runner.os == 'macOS'
shell: bash
run: ./build.dir/DSView.app/Contents/MacOS/DSView -v

- name: Package Linux
if: runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
version="${{ steps.meta.outputs.artifact_version }}"
stage="${RUNNER_TEMP}/stage"
mkdir -p "${stage}" dist
cmake --install build --prefix "${stage}/usr"
tar -C "${stage}" -czf "dist/DSView-${version}-${{ matrix.archive_name }}.tar.gz" usr
cpack --config build/CPackConfig.cmake -G DEB -B dist/cpack
deb_file="$(find dist/cpack -maxdepth 1 -type f -name '*.deb' | head -n 1)"
mv "${deb_file}" "dist/DSView-${version}-linux-amd64.deb"

- name: Package Windows
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
set -euo pipefail
version="${{ steps.meta.outputs.artifact_version }}"
dist_dir="$(pwd)/dist"
stage="${RUNNER_TEMP}/stage"
package_root="${RUNNER_TEMP}/DSView-${version}-${{ matrix.archive_name }}"
mkdir -p "${stage}" "${package_root}" dist
cmake --install build --prefix "${stage}"
cp -R "${stage}/bin/." "${package_root}/"
cp -R "${stage}/share/DSView/." "${package_root}/"
mkdir -p "${package_root}/decoders"
cp -R "${stage}/share/libsigrokdecode4DSL/decoders/." "${package_root}/decoders/"

deployqt="$(command -v windeployqt6.exe || command -v windeployqt.exe || command -v windeployqt6 || command -v windeployqt)"
"${deployqt}" --release --compiler-runtime "${package_root}/DSView.exe"

for dep in $(ldd "${package_root}/DSView.exe" | awk '/=> \\/mingw64\\// {print $3}'); do
cp -n "${dep}" "${package_root}/"
done

(cd "${RUNNER_TEMP}" && zip -r "${dist_dir}/DSView-${version}-${{ matrix.archive_name }}.zip" "$(basename "${package_root}")" >/dev/null)

- name: Package macOS
if: runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
version="${{ steps.meta.outputs.artifact_version }}"
stage="${RUNNER_TEMP}/stage"
app_path="${stage}/DSView.app"
mkdir -p "${stage}" dist
export PATH="$(brew --prefix qt@6)/bin:$PATH"

cmake --install build --prefix "${stage}"
mkdir -p "${app_path}/Contents/share"
cp -R "${stage}/share/DSView" "${app_path}/Contents/share/DSView"
cp -R "${stage}/share/libsigrokdecode4DSL" "${app_path}/Contents/share/libsigrokdecode4DSL"

macdeployqt "${app_path}" -dmg
ditto -c -k --sequesterRsrc --keepParent "${app_path}" "dist/DSView-${version}-${{ matrix.archive_name }}.zip"
mv "${stage}/DSView.dmg" "dist/DSView-${version}-${{ matrix.archive_name }}.dmg"

- name: Upload build artifact
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.artifact_name }}-${{ steps.meta.outputs.artifact_version }}
path: dist/*
if-no-files-found: error
retention-days: 14

release:
name: Publish GitHub release
if: github.ref_type == 'tag'
runs-on: ubuntu-24.04
needs:
- build

steps:
- name: Download packaged artifacts
uses: actions/download-artifact@v5
with:
path: release-artifacts

- name: Create or update release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME}"
title="DSView ${tag#v}"
if ! gh release view "${tag}" >/dev/null 2>&1; then
gh release create "${tag}" --title "${title}" --generate-notes
else
gh release edit "${tag}" --title "${title}"
fi
mapfile -d '' files < <(find release-artifacts -type f -print0)
gh release upload "${tag}" "${files[@]}" --clobber
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ moc_*.cpp_parameters

DSView-prj
build*
!.github/
!.github/workflows/
!.github/workflows/build-release.yml
share
.vscode
qtpro
Expand Down
Loading
Loading