# All-in-one continuous integration (CI) workflow. # Runs on all platforms (Windows, macOS, and Linux) # for all events (pull request, release, and schedule). name: CI on: push: branches: [master] tags: - 'v*' pull_request: types: - opened - reopened - synchronize - ready_for_review paths-ignore: - '**/*.md' - '.github/ISSUE_TEMPLATE/**' - '.editorconfig' - '.env-example' - '.gitignore' - '.gitattributes' - 'cspell.json' env: GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} PACKAGE_PREFIX: "deskflow" PACKAGE_PATH: ./dist CMAKE_ARGS: -DCMAKE_COMPILE_WARNING_AS_ERROR=ON jobs: # Always run this job, even if not on PR, since other jobs need it. pr-comment-flags: runs-on: ubuntu-latest needs: lint-clang outputs: no-sonar: ${{ steps.check.outputs.no-sonar }} steps: - name: Checkout uses: actions/checkout@v4 - name: Check PR comment for flags if: ${{ github.event_name == 'pull_request' }} id: check env: PR_BODY: ${{ github.event.pull_request.body }} run: | no_sonar="{no-sonar}" if echo $PR_BODY | grep -q "$no_sonar"; then echo "Flag $no_sonar found in PR body." echo "no-sonar=true" >> $GITHUB_OUTPUT else echo "No $no_sonar flag found in PR body." fi # Quality gate to allow PR merge, used in the branch protection rules. ci-passed: runs-on: ubuntu-latest needs: [test-results, unix] steps: - run: echo "✅ CI passed" > $GITHUB_STEP_SUMMARY # Summary of test results, combined from test result artifacts. # Runs even if the tests fail to provide a summary of the failures. test-results: needs: [windows, macos, linux] if: always() && needs.windows.result != 'skipped' && needs.macos.result != 'skipped' && needs.linux.result != 'skipped' runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@v4 - name: Test summary uses: ./.github/actions/test-summary lint-clang: runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@v4 - name: Linting Clang uses: ./.github/actions/lint-clang analyse-valgrind: needs: lint-clang if: ${{ github.event_name == 'pull_request' }} uses: ./.github/workflows/valgrind-analysis.yml analyse-sonarcloud: needs: pr-comment-flags if: ${{ needs.pr-comment-flags.outputs.no-sonar != 'true' }} uses: ./.github/workflows/sonarcloud-analysis.yml secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} windows: needs: lint-clang name: ${{ matrix.target.name }} runs-on: ${{ matrix.target.runs-on }} container: ${{ matrix.target.container }} # CI should normally takes less than 10 mins to run, # but if vcpkg has to rebuild the cache (should be rare), it takes about 90 mins. timeout-minutes: 120 strategy: # Normally, we want to fail fast, but in this case we shouldn't since one target may # fail due to transient issues unrelated to the build. fail-fast: false matrix: target: - name: windows-2022-x64 runs-on: ${{ vars.CI_WINDOWS_RUNNER || 'windows-2022' }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # The fetch-tags option for checkout does not work correctly so we need to do this. - name: Fetch tags run: | git config --global --add safe.directory $GITHUB_WORKSPACE git fetch --tags --force # This effectively runs `vcvarsall.bat`, etc. It's not actually installing # VC++ as that's already pre-installed on the Windows runner. - name: Setup VC++ environment uses: ilammy/msvc-dev-cmd@v1 # Install Ninja with an action instead of using Chocolatey, as it's more # reliable and faster. The Ninja install action is pretty good as it # downloads directly from the `ninja-build` GitHub project releases. - name: Install Ninja uses: seanmiddleditch/gha-setup-ninja@master - name: Dependencies (vcpkg) id: vcpkg uses: johnwason/vcpkg-action@v6 with: manifest-dir: ${{ github.workspace }} triplet: x64-windows-release token: ${{ github.token }} github-binarycache: true - name: Setup Python venv run: python ./scripts/setup_venv.py - name: Configure run: | cmake ${{ steps.vcpkg.outputs.vcpkg-cmake-config }} ` -B build -G Ninja ` -DCMAKE_BUILD_TYPE=Release ` ${{ env.CMAKE_ARGS }} - name: Build run: cmake --build build -j8 - name: Tests uses: ./.github/actions/run-tests timeout-minutes: 2 with: job: ${{ matrix.target.name }} - name: Get version uses: ./.github/actions/get-version - name: Package run: python ./scripts/package.py --package-version ${{env.DESKFLOW_VERSION}} env: WINDOWS_PFX_CERTIFICATE: ${{ secrets.WINDOWS_PFX }} WINDOWS_PFX_PASSWORD: ${{ secrets.WINDOWS_PFX_PASS }} - name: Upload uses: actions/upload-artifact@v4 with: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ env.PACKAGE_PATH }} macos: needs: lint-clang name: ${{ matrix.target.name }} runs-on: ${{ matrix.target.os }} timeout-minutes: ${{ matrix.target.timeout }} defaults: run: shell: ${{ matrix.target.shell }} strategy: # Normally, we want to fail fast, but in this case we shouldn't since one target may # fail due to transient issues unrelated to the build. fail-fast: false matrix: target: - name: "macos-14-arm64" timeout: 10 os: "macos-14" arch: arm64 shell: "/usr/bin/arch -arch arm64e /bin/bash --noprofile --norc -eo pipefail {0}" - name: ${{ vars.CI_MAC_INTEL_NAME || 'macos-13-x64' }} timeout: 20 os: ${{ vars.CI_MAC_INTEL_RUNNER || 'macos-13' }} arch: x64 shell: "bash" steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # The fetch-tags option for checkout does not work correctly so we need to do this. - name: Fetch tags run: | git config --global --add safe.directory $GITHUB_WORKSPACE git fetch --tags --force - name: Cache deps dir uses: actions/cache@v4 with: path: ./deps key: ${{ runner.os }}-deps-${{ hashFiles('config.yaml') }} - name: Install dependencies run: ./scripts/install_deps.py - name: Configure run: cmake -B build ${{ env.CMAKE_ARGS }} - name: Build run: cmake --build build -j8 - name: Tests uses: ./.github/actions/run-tests timeout-minutes: 2 with: job: ${{ matrix.target.name }} - name: Get version uses: ./.github/actions/get-version - name: Package run: ./scripts/package.py --package-version ${{env.DESKFLOW_VERSION}} env: APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} APPLE_P12_CERTIFICATE: ${{ secrets.APPLE_P12_CERTIFICATE }} APPLE_P12_PASSWORD: ${{ secrets.APPLE_P12_PASSWORD }} APPLE_NOTARY_USER: ${{ secrets.APPLE_NOTARY_USER }} APPLE_NOTARY_PASSWORD: ${{ secrets.APPLE_NOTARY_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - name: Upload uses: actions/upload-artifact@v4 with: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ env.PACKAGE_PATH }} linux-matrix: needs: lint-clang runs-on: ubuntu-latest outputs: json-matrix: ${{ steps.filter.outputs.json }} steps: - name: Checkout uses: actions/checkout@v4 - name: Filter JSON uses: ./.github/actions/filter-json id: filter with: json-file: .github/workflows/ci-linux.json condition: '"${{ vars.CI_USE_LINUX_ARM_RUNNER }}" != "true"' jq-filter: .distro |= map(select(.["runs-on"] | contains("arm64") | not)) linux: needs: linux-matrix name: linux-${{ matrix.distro.name }} runs-on: ${{ matrix.distro.runs-on }} container: ${{ matrix.distro.container }} timeout-minutes: 20 strategy: # Normally, we want to fail fast, but in this case we shouldn't since one distro may # fail due to transient issues unrelated to the build. fail-fast: false matrix: ${{fromJson(needs.linux-matrix.outputs.json-matrix)}} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # The fetch-tags option for checkout does not work correctly so we need to do this. - name: Fetch tags run: | git config --global --add safe.directory $GITHUB_WORKSPACE git fetch --tags --force - name: Add Kitware repo if: ${{ matrix.distro.name == 'ubuntu-22.04-amd64' }} uses: ./.github/actions/add-kitware-repo with: distro: jammy - name: Update CMake if: ${{ matrix.distro.name == 'ubuntu-22.04-amd64' }} run: apt install cmake -y - name: Install dependencies run: | ./scripts/install_deps.py env: # Prevent apt prompting for input. DEBIAN_FRONTEND: noninteractive - name: Configure run: | cmake -B build -G Ninja \ -DCMAKE_INSTALL_PREFIX=/usr \ ${{ env.CMAKE_ARGS }} \ ${{ matrix.distro.extra-cmake-args }} - name: Build package if: ${{ matrix.distro.arch-like != 'true'}} run: cmake --build build -j8 --target package - name: Build package (Arch-like) if: ${{ matrix.distro.arch-like == 'true' }} run: | cmake --build build -j8 sudo chown -R build build cd build sudo -u build makepkg -s export OSNAME=$(cat /etc/os-release | grep ^ID= | sed 's/ID=//g') export ARCH=$(uname -m) mv *.pkg.* $(ls *.pkg.* | sed "s/$ARCH/$OSNAME-$ARCH/g") cd .. - name: Tests uses: ./.github/actions/run-tests timeout-minutes: 2 with: job: linux-${{ matrix.distro.name }} - name: Upload uses: actions/upload-artifact@v4 with: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.distro.name }} path: ${{github.workspace}}/build/deskflow[-_]*.* # Technically, "unix" is a misnomer, but we use it here to mean "Unix-like BSD-derived". unix: needs: lint-clang name: unix-${{ matrix.distro.name }} runs-on: ${{ vars.CI_UNIX_RUNNER || 'ubuntu-24.04' }} timeout-minutes: 20 strategy: fail-fast: false matrix: distro: - name: freebsd steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # The fetch-tags option for checkout does not work correctly so we need to do this. - name: Fetch tags run: | git config --global --add safe.directory $GITHUB_WORKSPACE git fetch --tags --force - name: Build on FreeBSD if: ${{ matrix.distro.name == 'freebsd' }} uses: vmactions/freebsd-vm@v1 with: usesh: true run: | ./scripts/install_deps.sh cmake -B build ${{ env.CMAKE_ARGS }} cmake --build build -j16 # Integration tests are flakey by nature, make them optional. export QT_QPA_PLATFORM=offscreen ./build/bin/unittests ./build/bin/integtests || true release: needs: ci-passed runs-on: ubuntu-latest steps: - name: Download artifacts uses: actions/download-artifact@v4 - name: Deploy continuous if: (github.ref == 'refs/heads/master') && !(contains(github.ref, '/tags/v')) uses: crowbarmaster/GH-Automatic-Releases@latest with: repo_token: "${{ secrets.DEPLOY_TOKEN }}" automatic_release_tag: "continuous" prerelease: false title: "Continuous Build" files: | package-*/* - name: Deploy release if: contains(github.ref, '/tags/v') uses: crowbarmaster/GH-Automatic-Releases@latest with: repo_token: "${{ secrets.DEPLOY_TOKEN }}" prerelease: false files: | package-*/*