diff --git a/.github/workflows/deb-package.yml b/.github/workflows/deb-package.yml new file mode 100644 index 000000000..093cac8d8 --- /dev/null +++ b/.github/workflows/deb-package.yml @@ -0,0 +1,179 @@ +name: Build .deb Packages + +on: + push: + tags: + - "v*" + pull_request: + branches: + - main + paths: + - ".github/workflows/deb-package.yml" + - "CMakeLists.txt" + - "cmake/**" + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g. v1.2.3) — leave blank to build a test package" + required: false + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-deb: + name: Build .deb (${{ matrix.distro.label }}) + runs-on: ubuntu-latest + container: + image: ${{ matrix.distro.image }} + permissions: + contents: write + + strategy: + fail-fast: false + matrix: + distro: + - label: ubuntu-22.04 + image: ubuntu:22.04 + codename: jammy + qt_svg_pkg: libqt6svg6-dev + - label: ubuntu-24.04 + image: ubuntu:24.04 + codename: noble + qt_svg_pkg: qt6-svg-dev + - label: debian-12 + image: debian:bookworm + codename: bookworm + qt_svg_pkg: qt6-svg-dev + + steps: + - name: Resolve tag + id: meta + shell: bash + env: + GITHUB_EVENT_NAME: ${{ github.event_name }} + INPUT_TAG: ${{ inputs.tag }} + GITHUB_REF_NAME: ${{ github.ref_name }} + run: | + set -euo pipefail + TAG="" + IS_RELEASE="false" + + if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" && -n "${INPUT_TAG:-}" ]]; then + TAG="$INPUT_TAG" + elif [[ "$GITHUB_EVENT_NAME" == "push" && "$GITHUB_REF_NAME" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then + TAG="$GITHUB_REF_NAME" + fi + + if [[ -n "$TAG" ]]; then + if ! [[ "$TAG" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then + echo "Tag '$TAG' does not match expected format (e.g. v1.2.3)." >&2 + exit 1 + fi + VERSION="${TAG#v}" + IS_RELEASE="true" + else + VERSION="0.0.0~test" + fi + + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "version_tag=v${VERSION}" >> "$GITHUB_OUTPUT" + echo "is_release=$IS_RELEASE" >> "$GITHUB_OUTPUT" + echo "package=vicinae_${VERSION}_${{ matrix.distro.codename }}_amd64.deb" >> "$GITHUB_OUTPUT" + + - name: Install git + env: + DEBIAN_FRONTEND: noninteractive + run: | + apt-get update -y + apt-get install -y --no-install-recommends git ca-certificates + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ steps.meta.outputs.tag || github.sha }} + + - name: Install build dependencies + env: + DEBIAN_FRONTEND: noninteractive + run: | + apt-get install -y --no-install-recommends \ + build-essential cmake ninja-build pkg-config curl \ + libssl-dev libxml2-dev libminizip-dev libwayland-dev \ + qt6-base-dev qt6-base-private-dev qt6-declarative-dev \ + ${{ matrix.distro.qt_svg_pkg }} qt6-wayland-dev \ + libqalculate-dev \ + dpkg-dev + + - name: Install Node.js + env: + DEBIAN_FRONTEND: noninteractive + run: | + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + + - name: Configure CMake + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -DVICINAE_PROVENANCE=deb_package \ + -DVICINAE_GIT_TAG=${{ steps.meta.outputs.version_tag }} \ + -DUSE_SYSTEM_PROTOBUF=OFF \ + -DUSE_SYSTEM_ABSEIL=OFF \ + -DUSE_SYSTEM_CMARK_GFM=OFF \ + -DUSE_SYSTEM_LAYER_SHELL=OFF \ + -DUSE_SYSTEM_KF6=OFF \ + -DUSE_SYSTEM_QT_KEYCHAIN=OFF \ + -DCMAKE_INSTALL_PREFIX=/usr + + - name: Build + run: cmake --build build --parallel $(nproc) + + - name: Package with CPack + run: | + cd build + cpack -G DEB -D CPACK_PACKAGE_FILE_NAME="${{ steps.meta.outputs.package }}" + + - name: Verify .deb package + shell: bash + run: | + set -euo pipefail + DEB="build/${{ steps.meta.outputs.package }}" + + echo "=== Checking package file ===" + test -s "$DEB" || { echo "ERROR: .deb file not found or empty: $DEB" >&2; exit 1; } + ls -lh "$DEB" + + echo "=== Package metadata ===" + dpkg-deb --info "$DEB" + + echo "=== Package contents ===" + dpkg-deb --contents "$DEB" + + echo "=== Installing package ===" + apt-get install -y --no-install-recommends "./$DEB" + + echo "=== Verifying installed files ===" + test -x /usr/bin/vicinae \ + || { echo "ERROR: /usr/bin/vicinae not found after install" >&2; exit 1; } + echo "OK: /usr/bin/vicinae is present" + test -x /usr/libexec/vicinae/vicinae-server \ + || { echo "ERROR: /usr/libexec/vicinae/vicinae-server not found after install" >&2; exit 1; } + echo "OK: /usr/libexec/vicinae/vicinae-server is present" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.meta.outputs.package }} + path: build/${{ steps.meta.outputs.package }} + + - name: Upload to GitHub Release + if: steps.meta.outputs.is_release == 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.meta.outputs.tag }} + files: build/${{ steps.meta.outputs.package }} diff --git a/CMakeLists.txt b/CMakeLists.txt index b78bf664a..4c524b832 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,3 +271,19 @@ if (UNIX AND NOT APPLE) make_directory(${SYSTEMD_USER_DIR}) install(FILES ./extra/vicinae.service DESTINATION ${SYSTEMD_USER_DIR}) endif() + +# CPack .deb package configuration +string(REGEX REPLACE "^v" "" CPACK_PACKAGE_VERSION "${VICINAE_GIT_TAG}") +if(CPACK_PACKAGE_VERSION STREQUAL "") + set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") +endif() +set(CPACK_PACKAGE_NAME "vicinae") +set(CPACK_PACKAGE_VENDOR "vicinaehq") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A focused launcher for your desktop") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "vicinaehq ") +set(CPACK_DEBIAN_PACKAGE_SECTION "utils") +set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/vicinaehq/vicinae") +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +include(CPack) diff --git a/cmake/KF6.cmake b/cmake/KF6.cmake index 2c05f0ec2..f20f063f7 100644 --- a/cmake/KF6.cmake +++ b/cmake/KF6.cmake @@ -3,6 +3,41 @@ include(FetchContent) function(import_kf6) set(BUILD_SHARED_LIBS OFF) set(FETCHCONTENT_QUIET OFF) + + # ECM (extra-cmake-modules) 6.x is required by the KF6 syntax-highlighting + # CMakeLists.txt but is not yet packaged as 6.x on Ubuntu 22/24 or Debian 12. + # Fetch the source; then replace the generated ECMConfig.cmake (which + # hard-codes install-tree paths that do not exist) with one that points + # directly to the ECM source-tree module directories. + FetchContent_Declare( + ECM + GIT_REPOSITORY https://invent.kde.org/frameworks/extra-cmake-modules + GIT_TAG v6.20.0 + GIT_SHALLOW TRUE + EXCLUDE_FROM_ALL + ) + FetchContent_MakeAvailable(ECM) + + # Overwrite the broken build-tree ECMConfig.cmake with one that resolves + # module paths from the source directory (no installation required). + set(_ecm_modules "${ecm_SOURCE_DIR}/modules") + set(_ecm_find "${ecm_SOURCE_DIR}/find-modules") + set(_ecm_kde "${ecm_SOURCE_DIR}/kde-modules") + file(WRITE "${ecm_BINARY_DIR}/ECMConfig.cmake" + "set(ECM_VERSION \"6.20.0\")\n" + "set(ECM_VERSION_MAJOR 6)\n" + "set(ECM_VERSION_MINOR 20)\n" + "set(ECM_VERSION_PATCH 0)\n" + "set(ECM_MODULE_DIR \"${_ecm_modules}\")\n" + "set(ECM_FIND_MODULE_DIR \"${_ecm_find}\")\n" + "set(ECM_KDE_MODULE_DIR \"${_ecm_kde}\")\n" + "list(APPEND CMAKE_MODULE_PATH" + " \"${_ecm_modules}\"" + " \"${_ecm_find}\"" + " \"${_ecm_kde}\")\n" + ) + set(ECM_DIR "${ecm_BINARY_DIR}" CACHE PATH "Path to ECMConfig.cmake" FORCE) + FetchContent_Declare( KF6 GIT_REPOSITORY https://github.com/KDE/syntax-highlighting