Merge pull request #2053 from contour-terminal/build/libunicode-0.9.3 #8910
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| on: | |
| merge_group: | |
| push: | |
| paths-ignore: | |
| - 'docs/**' | |
| - '.github/ISSUE_TEMPLATE/**' | |
| - '.github/*.yml' | |
| - 'LICENSE.txt' | |
| - 'mkdocs.yml' | |
| - '*.md' | |
| - '*.sh' | |
| branches: | |
| - master | |
| pull_request: | |
| paths-ignore: | |
| - 'docs/**' | |
| - '.github/ISSUE_TEMPLATE/**' | |
| - '.github/*.yml' | |
| - 'LICENSE.txt' | |
| - 'mkdocs.yml' | |
| - '*.md' | |
| - '*.sh' | |
| branches: | |
| - master | |
| concurrency: | |
| group: build-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CTEST_OUTPUT_ON_FAILURE: 1 | |
| # Turn the workflow lint's offline skip into a hard failure. `check-workflows.py` skips when it | |
| # cannot obtain actionlint, so that a fresh offline clone still gets a green `ctest`; in CI that | |
| # would mean a gate quietly passing because its download failed, which is worse than no gate. | |
| # Set at workflow level so it covers every job here that runs `ctest` -- the Linux matrix and | |
| # packaging jobs via `--target test`, and the Windows job directly. | |
| CHECK_WORKFLOWS_REQUIRE_TOOL: 1 | |
| # Single source of truth for the LLVM/Clang toolchain version used across this workflow | |
| # (clang-format, clang-tidy and the Clang build matrix). Bump this one value to upgrade. | |
| LLVM_VERSION: "22" | |
| # Sccache GitHub Actions cache backend - currently disabled due to service reliability | |
| # See https://github.com/mozilla/sccache/blob/main/docs/GHA.md for documentation | |
| SCCACHE_GHA_ENABLED: "false" | |
| SCCACHE_GHA_VERSION: "20260106-1" | |
| jobs: | |
| # {{{ Common checks | |
| check_common: | |
| name: "Check CI scripts" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "Checking for common errors in build, deployment, and CI." | |
| run: ./scripts/check-common.sh | |
| check_PR_TODOs: | |
| name: "Check PR-TODOs" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "Checking for open PR-related TODO items" | |
| run: ./scripts/check-pr-todos.sh | |
| check_clang_format: | |
| name: "Check C++ style" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install clang | |
| run: | | |
| wget https://apt.llvm.org/llvm.sh | |
| chmod +x llvm.sh | |
| sudo ./llvm.sh "$LLVM_VERSION" | |
| sudo apt-get install "clang-format-$LLVM_VERSION" | |
| # The predicates must stay grouped: an ungrouped `-o` binds looser than any predicate added | |
| # later (e.g. -print0), which would silently reduce the file set instead of failing. | |
| # `*.h` is not dead weight: src/vtrasterizer/shared_defines.h keeps that extension because it | |
| # is shared with GLSL, and it is still a file we format. | |
| - name: "Clang-format" | |
| run: find ./src/ \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" \) -print0 | xargs -0 "clang-format-$LLVM_VERSION" --Werror --dry-run | |
| - name: "Check includes" | |
| run: ./scripts/check-includes.sh | |
| check_clang_tidy: | |
| name: "Check clang-tidy" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: "ccache-ubuntu2404-clang-tidy" | |
| max-size: 256M | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set environment variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - name: "install dependencies" | |
| run: sudo env QTVER=6 ./scripts/install-deps.sh | |
| - name: "Post-fix embedded dependency permissions." | |
| run: sudo find _deps/sources -exec chown $UID {} \; | |
| # Ubuntu 24.04's distro Qt (6.4.2) lacks the Qt6::GuiPrivate CMake package the RHI renderer needs; | |
| # install a modern Qt6 over it (see the build matrix job for the same rationale). | |
| - name: "Install Qt (GuiPrivate-capable)" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: "6.11.*" | |
| modules: qtmultimedia qt5compat qtshadertools qtspeech | |
| - name: Install clang | |
| run: | | |
| wget https://apt.llvm.org/llvm.sh | |
| chmod +x llvm.sh | |
| sudo ./llvm.sh ${LLVM_VERSION} | |
| sudo apt-get install clang-tidy-${LLVM_VERSION} | |
| # Catches stale/misspelled option keys and removed config directives in seconds, | |
| # rather than letting them silently no-op through a full tidy build. | |
| - name: "Verify clang-tidy configuration" | |
| run: CLANG_TIDY=clang-tidy-${LLVM_VERSION} ./scripts/check-clang-tidy-config.sh | |
| - name: "cmake" | |
| run: | | |
| cmake \ | |
| --preset clang-debug \ | |
| -B build \ | |
| -G Ninja \ | |
| -D CONTOUR_WAYLAND=OFF \ | |
| -D CMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" \ | |
| -D ENABLE_TIDY=ON \ | |
| -D CLANG_TIDY_EXE=/usr/bin/clang-tidy-${LLVM_VERSION} | |
| - name: "build" | |
| run: cmake --build build | |
| # Builds the GUI-less configuration, whose product is `contour daemon` -- a session host for a | |
| # server or a container. Being unbuilt is exactly how this configuration rotted before; one job | |
| # is what keeps a new GUI source from landing outside its CONTOUR_FRONTEND_GUI guard. | |
| # | |
| # Deliberately NOT installing a modern Qt over the distro one: relying on Ubuntu's Qt 6.4.2 | |
| # proves the headless build needs nothing beyond Qt6::Core -- no Qt6::GuiPrivate, no Qt Quick. | |
| # Clang only, since this gates a build configuration and not a toolchain. | |
| build_headless: | |
| name: "Ubuntu Linux (no GUI frontend)" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: "ccache-ubuntu2404-headless" | |
| max-size: 256M | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: "install dependencies" | |
| run: sudo env QTVER=6 ./scripts/install-deps.sh | |
| - name: "Post-fix embedded dependency permissions." | |
| run: sudo find _deps/sources -exec chown $UID {} \; | |
| - name: Install clang | |
| run: | | |
| wget https://apt.llvm.org/llvm.sh | |
| chmod +x llvm.sh | |
| sudo ./llvm.sh ${LLVM_VERSION} | |
| # CMAKE_DISABLE_FIND_PACKAGE_Qt6 makes every find_package(Qt6 ...) fail, so this configuration | |
| # cannot merely happen to build against a Qt that is lying around -- it has to want none. The | |
| # step above installed Qt, which is what makes the assertion worth anything: the point is not | |
| # that Qt is absent from the runner, it is that the build never reaches for it. Issue #2042 is | |
| # reported from a machine where Qt6 does not exist at all. | |
| - name: "cmake" | |
| run: | | |
| cmake \ | |
| --preset clang-debug \ | |
| -B build \ | |
| -G Ninja \ | |
| -D CONTOUR_WAYLAND=OFF \ | |
| -D CONTOUR_FRONTEND_GUI=OFF \ | |
| -D CONTOUR_TESTING=ON \ | |
| -D CMAKE_DISABLE_FIND_PACKAGE_Qt6=ON \ | |
| -D CMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" | |
| - name: "build" | |
| run: cmake --build build --target contour contour_test | |
| # ...and that the linked result carries no Qt either, which the configure-time gate above cannot | |
| # see: a transitive Qt6::Core from some other target would still land here. | |
| - name: "verify the binary links no Qt" | |
| run: | | |
| set -eu | |
| if ldd build/src/contour/contour | grep -i "libQt"; then | |
| echo "::error::GUI-less build links Qt" | |
| exit 1 | |
| fi | |
| # contour_test is the unit suite for the layers this configuration is made of -- the | |
| # configuration model, the command vocabulary and the CLI -- and it links no Qt, which is the | |
| # only reason it can run here at all. Before it existed, this job built the binary and asserted | |
| # nothing about its behaviour. | |
| # | |
| # contour_e2e_cli_verbs asserts the verb surface this configuration is supposed to expose -- | |
| # `daemon` present, `client` gone. contour_e2e_shell_integration asserts that `generate | |
| # integration` still emits the scripts verbatim, which is the verb that used to read them out of | |
| # a Qt resource. All three are ctest tests rather than steps of this job, so they also run in the | |
| # ordinary GUI builds, where they assert the other half. | |
| - name: "tests" | |
| run: ctest --test-dir build -R "contour_test|contour_e2e_cli_verbs|contour_e2e_shell_integration" --output-on-failure | |
| # Building is not the same as being installable: this configuration reached issue #1780 with a | |
| # `contour` binary that compiled and an install target that errored out on a missing one. The | |
| # install rules are a per-configuration decision, so they need a per-configuration run. | |
| # --component contour, because the build step above builds the `contour` target alone: a full | |
| # install would also want vtbackend's bench-headless, which was never compiled. The component is | |
| # exactly the set of rules this job is here to exercise. | |
| - name: "install" | |
| run: | | |
| set -eu | |
| cmake --install build --prefix "$RUNNER_TEMP/contour-headless" --component contour | |
| test -x "$RUNNER_TEMP/contour-headless/bin/contour" | |
| test -f "$RUNNER_TEMP/contour-headless/share/contour/shell-integration/shell-integration.zsh" | |
| # The macOS half of the same guard. A .app bundle, macdeployqt and a signed .dmg all serve a | |
| # window; a GUI-less build has none, so it must install a plain bin/contour like any other Unix | |
| # CLI tool. The Linux job above cannot assert that -- it never enters CMake's APPLE branch. | |
| # | |
| # Configure-only on purpose: what this checks is which rules the configuration GENERATES, and | |
| # every way it can go wrong (a REQUIRED find_package for a GUI module, a macdeployqt lookup, an | |
| # install rule that stages a bundle) is decided at configure time. Compiling the same headless | |
| # sources a second time would only repeat the job above at several times the cost. | |
| build_headless_macos: | |
| name: "macOS (no GUI frontend)" | |
| runs-on: macos-15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # CONTOUR_INSTALL_BREW_QT: install-deps.sh leaves Qt out on macOS because Homebrew's split | |
| # per-module prefixes break macdeployqt for release bundles. This build makes no bundle, so the | |
| # Homebrew Qt is exactly the "dev build that is never packaged" case that script documents. | |
| # It is installed here even though this configuration must not use it, for the same reason as in | |
| # the Linux job: a Qt that is present and still untouched is the assertion worth making. | |
| - name: "install dependencies" | |
| run: env CONTOUR_INSTALL_BREW_QT=ON QTVER=6 ./scripts/install-deps.sh | |
| - name: "cmake" | |
| run: | | |
| cmake \ | |
| --preset appleclang-debug \ | |
| -B build \ | |
| -D CONTOUR_FRONTEND_GUI=OFF \ | |
| -D CONTOUR_TESTING=OFF \ | |
| -D CMAKE_DISABLE_FIND_PACKAGE_Qt6=ON | |
| - name: "verify no app-bundle machinery was generated" | |
| run: | | |
| set -eu | |
| for pattern in macdeployqt contour.app DragNDrop MacOSNotarizeApp MacOSSignDmg; do | |
| if grep -qF "$pattern" build/src/contour/cmake_install.cmake build/CPackConfig.cmake; then | |
| echo "::error::GUI-less macOS build still generates '$pattern' rules" | |
| exit 1 | |
| fi | |
| done | |
| # ...and that it still installs SOMETHING: the rules must have moved to the CLI branch, not | |
| # gone missing with the bundle they used to live next to. | |
| if ! grep -q 'CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE' build/src/contour/cmake_install.cmake; then | |
| echo "::error::GUI-less macOS build installs no executable into bin/" | |
| exit 1 | |
| fi | |
| check_links: | |
| name: "Check links" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run linksafe | |
| uses: Yaraslaut/linksafe@main | |
| with: # comma separated lists | |
| # use relative paths, if no dirs specified root dir is scanned | |
| ignored_dirs: "_deps" | |
| ignored_files: "./scripts/install-deps.sh, | |
| ./scripts/install-deps.ps1" | |
| ignored_links: "https://www.contributor-covenant.org/faq, | |
| https://www.contributor-covenant.org, | |
| https://flatpak.org/getting.html, | |
| https://codeberg.org/dnkl/foot, | |
| https://codecov.io/gh/contour-terminal/contour, | |
| https://msdn.microsoft.com/zh-cn/library/f4k12ae8, | |
| https://msdn.microsoft.com/en-us/library/fzt08www, | |
| " | |
| editorconfig: | |
| name: "Check editorconfig" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: editorconfig-checker/action-editorconfig-checker@main | |
| - run: editorconfig-checker | |
| # }}} | |
| # {{{ StaticBuild | |
| staticBuild: | |
| if: github.ref == 'refs/heads/master' || github.head_ref == 'release' | |
| name: "Static build" | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v2 | |
| with: | |
| platforms: all | |
| - name: Set up Docker Buildx | |
| id: buildx | |
| uses: docker/setup-buildx-action@v2 | |
| with: | |
| version: latest | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: Build release | |
| shell: bash | |
| run: | | |
| docker buildx build \ | |
| --tag static_contour \ | |
| --progress=plain \ | |
| -f .github/static/DockerUbuntu \ | |
| --load \ | |
| . | |
| docker create --name contour_static static_contour | |
| docker cp contour_static:/contour/build/src/contour/contour contour | |
| docker container rm contour_static | |
| - name: "Uploading static executable" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "contour" | |
| path: "contour" | |
| if-no-files-found: error | |
| retention-days: 1 | |
| # }}} | |
| # {{{ Fedora | |
| # fedora: | |
| # strategy: | |
| # fail-fast: false | |
| # matrix: | |
| # os_version: [40] | |
| # arch: | |
| # [ | |
| # "linux/amd64 x86_64" | |
| # ] | |
| # name: "Fedora ${{ matrix.os_version }} ${{ matrix.arch }}" | |
| # runs-on: ubuntu-latest | |
| # steps: | |
| # - uses: actions/checkout@v4 | |
| # - name: Set up Docker Buildx | |
| # id: buildx | |
| # uses: docker/setup-buildx-action@v2 | |
| # with: | |
| # version: latest | |
| # - name: Read matrix info | |
| # id: tags | |
| # shell: bash | |
| # run: | | |
| # arch="${{ matrix.arch }}" | |
| # echo "PLATFORM=${arch%% *}" >> "$GITHUB_OUTPUT" | |
| # echo "ARCH=${arch##* }" >> "$GITHUB_OUTPUT" | |
| # - name: "update APT database" | |
| # run: sudo apt -q update | |
| # - name: Installing xmllint for ci-set-vars | |
| # run: sudo apt -qy install libxml2-utils | |
| # - name: set environment variables | |
| # id: set_vars | |
| # run: ./scripts/ci-set-vars.sh | |
| # env: | |
| # REPOSITORY: ${{ github.event.repository.name }} | |
| # - name: Fetch and unpack embeds | |
| # run: ./scripts/install-deps.sh | |
| # env: | |
| # PREPARE_ONLY_EMBEDS: 'ON' | |
| # SYSDEP_ASSUME_YES: 'ON' | |
| # OS_OVERRIDE: 'fedora' | |
| # - name: "Post-fix embedded dependency permissions." | |
| # run: sudo find _deps/sources -exec chown $UID {} \; | |
| # - name: prepare distfile | |
| # run: | | |
| # set -x | |
| # PKGNAME="contour-${{ steps.set_vars.outputs.VERSION }}" | |
| # DISTDIR="/tmp/dist/${PKGNAME}" | |
| # mkdir -p ${DISTDIR} | |
| # cp -rvp . ${DISTDIR} | |
| # tar czpf ${PKGNAME}.tar.gz -C "/tmp/dist" . | |
| # - name: disable pedantic compiler on certain OS versions | |
| # # Generally disable -Werror. | |
| # #if: ${{ matrix.os_version == 38 || matrix.os_version == 39 }} | |
| # run: | | |
| # set -ex | |
| # #sed -i -e "s/PEDANTIC_COMPILER=ON/PEDANTIC_COMPILER=OFF/" .github/fedora/contour.spec | |
| # sed -i -e "s/PEDANTIC_COMPILER_WERROR=ON/PEDANTIC_COMPILER_WERROR=OFF/" .github/fedora/contour.spec | |
| # - name: Build ${{ matrix.arch }} release | |
| # shell: bash | |
| # run: | | |
| # set -x | |
| # ARCH="${{ steps.tags.outputs.ARCH }}" | |
| # VERSION="${{ steps.set_vars.outputs.VERSION }}" | |
| # OS_VERSION="${{ matrix.os_version }}" | |
| # sed -i -e "s/fedora:35/fedora:${{ matrix.os_version }}/" .github/fedora/Dockerfile | |
| # docker buildx build --platform ${{ steps.tags.outputs.PLATFORM }} \ | |
| # --tag contour:${ARCH} \ | |
| # --build-arg VERSION=${VERSION} \ | |
| # --build-arg VERSION_STRING=${VERSION} \ | |
| # -f .github/fedora/Dockerfile \ | |
| # --load \ | |
| # . | |
| # docker create --name contour-${ARCH} contour:${ARCH} | |
| # docker cp contour-${ARCH}:/app/rpmbuild/RPMS/${ARCH}/contour-${VERSION}-1.fc${OS_VERSION}.${ARCH}.rpm \ | |
| # contour-${VERSION}-1.fc${OS_VERSION}.${ARCH}.rpm | |
| # docker cp contour-${ARCH}:/app/rpmbuild/RPMS/${ARCH}/contour-debuginfo-${VERSION}-1.fc${OS_VERSION}.${ARCH}.rpm \ | |
| # contour-debuginfo-${VERSION}-1.fc${OS_VERSION}.${ARCH}.rpm | |
| # docker cp contour-${ARCH}:/app/rpmbuild/RPMS/${ARCH}/contour-debugsource-${VERSION}-1.fc${OS_VERSION}.${ARCH}.rpm \ | |
| # contour-debugsource-${VERSION}-1.fc${OS_VERSION}.${ARCH}.rpm | |
| # docker container rm contour-${{ steps.tags.outputs.ARCH }} | |
| # echo "pwd: `pwd`" && ls -hla | |
| # - name: "Uploading Fedora RPM package" | |
| # uses: actions/upload-artifact@v4 | |
| # with: | |
| # name: "contour-${{ steps.set_vars.outputs.VERSION }}-1.fc${{ matrix.os_version }}.${{ steps.tags.outputs.ARCH }}.rpm" | |
| # path: | | |
| # contour-${{ steps.set_vars.outputs.VERSION }}-1.fc${{ matrix.os_version }}.${{ steps.tags.outputs.ARCH }}.rpm | |
| # contour-debuginfo-${{ steps.set_vars.outputs.VERSION }}-1.fc${{ matrix.os_version }}.${{ steps.tags.outputs.ARCH }}.rpm | |
| # contour-debugsource-${{ steps.set_vars.outputs.VERSION }}-1.fc${{ matrix.os_version }}.${{ steps.tags.outputs.ARCH }}.rpm | |
| # if-no-files-found: error | |
| # retention-days: 7 | |
| # }}} | |
| # {{{ FreeBSD | |
| freebsd: | |
| if: github.ref == 'refs/heads/master' | |
| runs-on: ubuntu-latest | |
| name: FreeBSD 13 | |
| # env: | |
| # MYTOKEN: "value1" | |
| # MYTOKEN2: "value2" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Test in FreeBSD | |
| id: test | |
| uses: vmactions/freebsd-vm@v1 | |
| timeout-minutes: 60 | |
| with: | |
| envs: 'MYTOKEN MYTOKEN2' | |
| usesh: true | |
| copyback: false | |
| prepare: pkg install -y wget | |
| run: | | |
| set -ex | |
| pwd | |
| ls -lah | |
| whoami | |
| env | |
| freebsd-version | |
| SYSDEP_ASSUME_YES=ON ./scripts/install-deps.sh | |
| cmake -S . -B build -DCONTOUR_TESTING=ON -DLIBUNICODE_USE_STD_SIMD=OFF | |
| cmake --build build/ -j2 | |
| # ctest, not a hand-written list: that list predated src/coro, src/net, src/vthost and | |
| # src/vtworkspace, so those suites were built in this VM and thrown away -- and a BSD | |
| # is exactly where the daemon's socket code would diverge. The label exclusions keep | |
| # the VM's budget: `e2e` boots the real app repeatedly, and `lint` re-runs gates the | |
| # Linux jobs already own. | |
| # | |
| # contour_gui_test is excluded BY NAME, not because it cannot work here -- it selects | |
| # the offscreen QPA itself when there is no DISPLAY -- but because it has never once | |
| # run on this VM, and this job gates master rather than a PR. Widening coverage to the | |
| # daemon suites should not also stake master on an untried Qt run. Drop the exclusion | |
| # once someone has watched it pass here. | |
| ctest --test-dir build/ --output-on-failure -LE "e2e|lint" -E "contour_gui_test" | |
| rm -rf _deps build | |
| # }}} | |
| # {{{ OpenBSD | |
| openbsd: | |
| if: false #github.ref == 'refs/heads/master' || github.head_ref == 'release' | |
| runs-on: ubuntu-latest | |
| name: OpenBSD 7.5 | |
| # env: | |
| # MYTOKEN: "value1" | |
| # MYTOKEN2: "value2" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Test in OpenBSD | |
| id: test | |
| uses: vmactions/openbsd-vm@v1 | |
| timeout-minutes: 60 | |
| with: | |
| envs: 'MYTOKEN MYTOKEN2' | |
| usesh: true | |
| copyback: false | |
| prepare: pkg_add -DI wget | |
| run: | | |
| set -ex | |
| pwd | |
| ls -lah | |
| whoami | |
| env | |
| ./scripts/install-deps.sh | |
| mkdir build | |
| cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++ -D CMAKE_CXX_FLAGS=-I/home/runner/work/contour/contour/_deps/sources/yaml-cpp-0.8.0/include -DCONTOUR_TESTING=ON -DLIBUNICODE_USE_STD_SIMD=OFF | |
| cmake --build build/ -j2 | |
| ./build/src/crispy/crispy_test | |
| ./build/src/vtparser/vtparser_test | |
| ./build/src/vtbackend/vtbackend_test | |
| ./build/src/vtrasterizer/vtrasterizer_test | |
| rm -rf _deps build | |
| # }}} | |
| # {{{ macOS | |
| macOSArm: | |
| # Pinned, not macos-latest, so the toolchain and SDK behind a release are reproducible | |
| # and a GitHub image rotation cannot silently change what ships. | |
| # | |
| # The minimum macOS of the .dmg no longer depends on this: the libraries Contour links | |
| # are built from source by vcpkg against CMAKE_OSX_DEPLOYMENT_TARGET (13.3), not taken | |
| # from the runner's Homebrew bottles. CONTOUR_MACOS_MIN_SUPPORTED enforces that. | |
| runs-on: macos-15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: set variables | |
| id: set_vars | |
| run: | | |
| ./scripts/ci-set-vars.sh | |
| # Signing is gated on whether the secrets are actually reachable, not on the | |
| # branch name. GitHub withholds secrets from fork pull requests, which is the | |
| # real precondition; a branch-name test only approximates it, and approximated | |
| # it badly in both directions -- fork PRs still asked codesign for a Developer ID | |
| # that was in no keychain (breaking every PR run of this job), while a release | |
| # branch's signing path went completely unexercised until it was already on | |
| # master. Signing has to be proven before the merge, not after it. | |
| if [[ -n "$HAS_SIGNING_SECRETS" ]]; then | |
| echo "CODE_SIGN_CERTIFICATE_ID=Developer ID Application: Christian Parpart (6T525MU9UR)" >> "$GITHUB_OUTPUT" | |
| echo "NOTARIZE=ON" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "CODE_SIGN_CERTIFICATE_ID=-" >> "$GITHUB_OUTPUT" | |
| echo "NOTARIZE=OFF" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Stapled together with the image, never separately. A ticket on the .dmg alone | |
| # covers the download, but the app dragged out of it carries none -- so its first | |
| # launch depends on Gatekeeper reaching Apple, and a user who is offline, behind a | |
| # captive portal or on a restricted network gets exactly the dialog this pipeline | |
| # exists to prevent: "Apple could not verify ... is free of malware". Stapling the | |
| # app makes the installed copy self-sufficient. It costs a second Apple round trip | |
| # of a minute or two, which is not a price worth haggling over against that. | |
| if [[ -n "$HAS_SIGNING_SECRETS" ]]; then | |
| echo "STAPLE_APP=ON" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "STAPLE_APP=OFF" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| # Via the environment, not interpolated into the script: a branch name is | |
| # attacker-controlled on a fork's pull request. | |
| GH_REF: ${{ github.ref }} | |
| GH_HEAD_REF: ${{ github.head_ref }} | |
| # Empty on fork pull requests, where GitHub withholds secrets. Only the | |
| # emptiness is observable here -- the value never reaches the log. | |
| HAS_SIGNING_SECRETS: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| - name: Install the Apple signing certificate | |
| if: steps.set_vars.outputs.NOTARIZE == 'ON' | |
| env: | |
| BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| P12_PASSWORD: ${{ secrets.P12_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| run: | | |
| # @see https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development | |
| set -eu | |
| CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| # -T grants codesign access to the imported key, and set-key-partition-list | |
| # records that grant in the key's ACL. Without both, codesign blocks on a GUI | |
| # confirmation prompt that never arrives on a runner, and the job hangs until | |
| # its timeout rather than reporting anything useful. | |
| security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 \ | |
| -k $KEYCHAIN_PATH -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: \ | |
| -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH | |
| security find-identity -v -p codesigning $KEYCHAIN_PATH | |
| - name: Store the notarization credentials | |
| if: steps.set_vars.outputs.NOTARIZE == 'ON' | |
| env: | |
| APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }} | |
| run: | | |
| # An App Store Connect API key rather than an Apple ID and app-specific | |
| # password: it carries no personal account, can be scoped to the Developer | |
| # role, and is revocable on its own. | |
| set -eu | |
| KEY_PATH=$RUNNER_TEMP/notary-key.p8 | |
| echo -n "$APPLE_API_KEY_P8" | base64 --decode -o $KEY_PATH | |
| xcrun notarytool store-credentials contour-notary \ | |
| --key "$KEY_PATH" \ | |
| --key-id "$APPLE_API_KEY_ID" \ | |
| --issuer "$APPLE_API_ISSUER_ID" | |
| rm -f "$KEY_PATH" | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: ccache-macosArm-r1 | |
| max-size: 256M | |
| - name: Install Qt | |
| # The official Qt binaries, as on Linux and Windows -- not Homebrew's. Homebrew | |
| # splits Qt across ~40 per-module prefixes and macdeployqt only follows one of | |
| # them, so a brew-Qt bundle ships missing frameworks and unresolvable @rpath | |
| # references. That is the defect this job used to hand to users as a .dmg. | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: 6.11.* | |
| modules: qtmultimedia qt5compat qtshadertools qtspeech | |
| cache: true | |
| - name: "Install dependencies" | |
| # Sometimes, brew thinks it needs to install from source rather than binary, | |
| # so cap the runtime rather than burn CI credits on a source build. | |
| # | |
| # The libraries Contour links are NOT taken from Homebrew here -- see the vcpkg | |
| # steps below for why. install-deps.sh also brings the autotools that some vcpkg | |
| # ports need on the build host. | |
| timeout-minutes: 15 | |
| run: | | |
| set -ex | |
| #brew update | |
| # Trying to work around some Github CI issues causing `brew install` to fail | |
| brew uninstall aws-sam-cli azure-cli | |
| ./scripts/install-deps.sh | |
| - name: "Cache vcpkg" | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cache/vcpkg/archives | |
| key: vcpkg-macos-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json', 'cmake/vcpkg-triplets/arm64-osx-contour.cmake') }} | |
| restore-keys: vcpkg-macos- | |
| - name: "vcpkg: clone and bootstrap" | |
| # The libraries Contour links (openssl, libssh2, yaml-cpp, freetype, harfbuzz, | |
| # cairo) are built from source against CMAKE_OSX_DEPLOYMENT_TARGET rather than | |
| # taken from Homebrew. Homebrew ships one prebuilt bottle per macOS release and | |
| # installs the one matching the runner, which made the .dmg's minimum macOS equal | |
| # to the runner image's -- an invisible compatibility cliff that moved whenever | |
| # GitHub rotated the image. vcpkg puts that floor under our control; the triplet at | |
| # cmake/vcpkg-triplets/arm64-osx-contour.cmake is where it is set. | |
| run: | | |
| set -ex | |
| git clone --depth 1 https://github.com/microsoft/vcpkg.git "$RUNNER_TEMP/vcpkg" | |
| "$RUNNER_TEMP/vcpkg/bootstrap-vcpkg.sh" -disableMetrics | |
| echo "VCPKG_ROOT=$RUNNER_TEMP/vcpkg" >> "$GITHUB_ENV" | |
| - name: "install tmux (oracle for the tmux-interop tests)" | |
| # A CI-only tool, so it is here rather than in install-deps.sh, which lists what Contour | |
| # needs to BUILD. Without it the tmux oracles self-skip, and switching this job to | |
| # ctest (below) would otherwise have reached them only to watch them do nothing -- macOS | |
| # is where the BSD socket and Apple-libc++ differences in that code path would show up. | |
| # | |
| # Built from source at the same pinned 3.7b as the Linux matrix, rather than taken from the | |
| # formula. Both platforms then measure against the exact protocol version the control-mode | |
| # server claims to speak (docs/internals/vthost.md), and neither can start or stop running | |
| # the imsg oracle because a formula moved. | |
| run: | | |
| set -ex | |
| # tmux's configure hard-errors without libevent; ncurses comes with the SDK on macOS. | |
| # PKG_CONFIG_PATH is stated rather than assumed: the formula is not keg-only today, so its | |
| # .pc file is normally on the default search path anyway -- but naming the prefix keeps | |
| # this working if that ever changes, and costs nothing while it has not. | |
| brew install libevent | |
| export PKG_CONFIG_PATH="$(brew --prefix libevent)/lib/pkgconfig:${PKG_CONFIG_PATH:-}" | |
| curl -sSL -o "$RUNNER_TEMP/tmux.tar.gz" \ | |
| https://github.com/tmux/tmux/releases/download/3.7b/tmux-3.7b.tar.gz | |
| tar xzf "$RUNNER_TEMP/tmux.tar.gz" -C "$RUNNER_TEMP" | |
| # The release tarball ships a generated configure, so no autotools are needed here. | |
| # --disable-utf8proc is required, not merely a choice: 3.7b's configure hard-errors | |
| # ("must give --enable-utf8proc or --disable-utf8proc") rather than defaulting, and this | |
| # oracle only has to speak the control-mode protocol -- how tmux measures east-Asian | |
| # width does not enter into it. Stated on both platforms so they build the same binary. | |
| cd "$RUNNER_TEMP/tmux-3.7b" \ | |
| && ./configure --disable-utf8proc \ | |
| && make -j"$(sysctl -n hw.ncpu)" \ | |
| && sudo make install | |
| tmux -V | |
| - name: "Generate build files" | |
| run: | | |
| # The `macos-package` preset IS the definition of a shippable macOS build -- | |
| # Qt prefix, deployment target, warning policy, what lands in the bundle. This | |
| # job overrides only the two things a preset cannot know: which identity is | |
| # available in this run's keychain, and whether notarization secrets exist. | |
| # Everything else must come from the preset, or the .dmg users download would | |
| # again be built by a configuration nothing else exercises. | |
| # | |
| # QT_ROOT_DIR is exported by install-qt-action and is what the preset's | |
| # CMAKE_PREFIX_PATH reads. | |
| # The preset carries CONTOUR_MACOS_MIN_SUPPORTED=13.3, and it is enforced: if the | |
| # bundled dylibs demand anything newer, the package step fails rather than | |
| # shipping a .dmg that cannot launch. Configuring also triggers the vcpkg | |
| # manifest install, which builds those dylibs against that same target. | |
| cmake --preset macos-package \ | |
| -DCODE_SIGN_CERTIFICATE_ID="${{ steps.set_vars.outputs.CODE_SIGN_CERTIFICATE_ID }}" \ | |
| -DCONTOUR_MACOS_NOTARIZE="${{ steps.set_vars.outputs.NOTARIZE }}" \ | |
| -DCONTOUR_MACOS_STAPLE_APP="${{ steps.set_vars.outputs.STAPLE_APP }}" | |
| - name: "Build" | |
| run: cmake --build --preset macos-package | |
| - name: "tests" | |
| # ctest, not a hand-written list of binaries: the previous four-line list predated | |
| # src/coro, src/net, src/vthost and src/vtworkspace, so those suites were built here and | |
| # never run — macOS is exactly where the Apple-libc++ and BSD-socket differences bite. | |
| # Registering a new test target must not require editing this file. | |
| # | |
| # 15 rather than 10: with tmux installed the four oracles now actually run instead of | |
| # skipping, and each drives a real tmux server over a pty. | |
| timeout-minutes: 15 | |
| run: ctest --preset macos-package | |
| - name: "verify the tmux oracles actually ran" | |
| # @see the identical assertion on the Linux matrix for why a green suite is not enough: | |
| # a Catch2 SKIP exits 0, so "no tmux, nothing ran" is indistinguishable from success. | |
| # Nothing is excluded, for parity with Linux: this job pins the same tmux 3.7b, so the imsg | |
| # oracle runs here too rather than being carved out by name. Pinning is what makes that safe | |
| # -- the version is stated in the workflow, not inherited from whatever a formula moved to. | |
| run: | | |
| set -o pipefail | |
| binary=out/macos-package/src/vthost/vthost_test | |
| output=$("$binary" '[oracle]' 2>&1) || { printf '%s\n' "$output"; exit 1; } | |
| printf '%s\n' "$output" | |
| if printf '%s' "$output" | grep -q 'skipped'; then | |
| echo "::error::$binary reported SKIPPED; the tmux interop tests are not being exercised on macOS" | |
| exit 1 | |
| fi | |
| - name: "Create Package(s)" | |
| # cpack deploys, prunes unloadable plugins, verifies the bundle is self-contained, | |
| # signs it inside-out, notarizes and staples both the app and the image. Each of | |
| # those steps is fatal on failure now, so a broken bundle stops here instead of | |
| # being uploaded. Notarization waits on Apple, hence the generous timeout. | |
| timeout-minutes: 30 | |
| run: | | |
| set -ex | |
| echo killing...; sudo pkill -9 XProtect >/dev/null || true; # see https://github.com/actions/runner-images/issues/7522 | |
| echo waiting...; while pgrep XProtect; do sleep 3; done; | |
| cpack --preset macos-package --verbose | |
| BASENAME="contour-${{ steps.set_vars.outputs.VERSION_STRING }}-macOS-arm" | |
| mv -vf "out/macos-package/Contour-${{ steps.set_vars.outputs.VERSION_STRING }}-Darwin.dmg" "${BASENAME}.dmg" | |
| echo "DMG=${BASENAME}.dmg" >> "$GITHUB_ENV" | |
| - name: "verify the DMG is something a user can actually open" | |
| # The assertion that was missing all along: a green package step used to mean only | |
| # that hdiutil succeeded. | |
| # | |
| # Scoped to what cpack could not already check. The image's own signature, its | |
| # staple and `spctl --type install` are asserted fatally inside the packaging run | |
| # (macos-bundle.py sign/notarize and cmake/MacOSSignDmg.cmake), and the .dmg here | |
| # is the byte-identical file cpack produced -- only `mv`'d. What is genuinely new | |
| # is the app *as mounted from the image*, a different object from the staged | |
| # bundle that was signed. | |
| run: | | |
| set -ex | |
| MOUNT=$(mktemp -d) | |
| hdiutil attach -nobrowse -readonly -mountpoint "$MOUNT" "$DMG" | |
| trap 'hdiutil detach "$MOUNT" || true' EXIT | |
| codesign --verify --deep --strict --verbose=4 "$MOUNT/contour.app" | |
| # The image ships the app and nothing else -- plus the /Applications symlink the | |
| # DragNDrop generator adds for drag-installing. CONTOUR_INSTALL_TOOLS=OFF in the | |
| # preset is what keeps bench-headless out of Contents/MacOS; assert it, because | |
| # the day someone flips that option back on, the only visible symptom would be a | |
| # developer benchmark tool shipping inside a user-facing release. | |
| test "$(ls "$MOUNT" | sort | tr '\n' ' ')" = "Applications contour.app " | |
| test "$(ls "$MOUNT/contour.app/Contents/MacOS")" = "contour" | |
| if [[ "${{ steps.set_vars.outputs.NOTARIZE }}" != 'ON' ]]; then | |
| echo "::notice::unsigned build (no secrets); skipping Gatekeeper assertions" | |
| exit 0 | |
| fi | |
| # Reproduce what the user actually does, because that is where the | |
| # "Apple could not verify ... is free of malware" dialog comes from: they drag | |
| # the app OUT of the image and launch the copy. A ticket stapled only to the | |
| # .dmg does not travel with that copy, leaving first launch dependent on | |
| # Gatekeeper reaching Apple -- which fails offline, behind a captive portal, or | |
| # on a restricted network. So the checks below run against the copy, not against | |
| # the app sitting on the mounted image. | |
| INSTALLED=$(mktemp -d)/contour.app | |
| ditto "$MOUNT/contour.app" "$INSTALLED" | |
| # A downloaded image carries this; the copy must clear Gatekeeper with it set. | |
| xattr -w com.apple.quarantine "0081;00000000;Safari;" "$INSTALLED" | |
| # The ticket is embedded in the copy, so first launch needs no network at all. | |
| xcrun stapler validate "$INSTALLED" | |
| # The exact decision Gatekeeper makes when that copy is first launched. | |
| spctl --assess --verbose=4 --type exec "$INSTALLED" | |
| # And the same for the image a browser just downloaded. | |
| xcrun stapler validate "$DMG" | |
| spctl --assess --verbose=4 --type install "$DMG" | |
| - name: upload to artifact store (DMG) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-macOS-arm.dmg" | |
| path: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-macOS-arm.dmg" | |
| retention-days: 7 | |
| # }}} | |
| # {{{ Windows | |
| windows: | |
| name: "Windows" | |
| runs-on: windows-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| env: | |
| CMAKE_PRESET: "msvc-release" | |
| VCPKG_BINARY_SOURCES: "clear;nuget,GitHub,readwrite" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run sccache-cache | |
| if: env.SCCACHE_GHA_ENABLED == 'true' | |
| uses: mozilla-actions/sccache-action@v0.0.3 | |
| continue-on-error: true | |
| id: sccache | |
| - name: "Verify sccache is operational" | |
| if: env.SCCACHE_GHA_ENABLED == 'true' | |
| id: verify_sccache | |
| shell: pwsh | |
| run: | | |
| # If GHA cache is disabled, force local disk cache | |
| # Otherwise, allow the sccache-action's default configuration | |
| if ($env:SCCACHE_GHA_ENABLED -eq "false") { | |
| Write-Host "GHA cache disabled, forcing local disk cache" | |
| echo "SCCACHE_GHA_ENABLED=" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| echo "SCCACHE_DIR=$env:LOCALAPPDATA\Mozilla\sccache" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| $env:SCCACHE_GHA_ENABLED = "" | |
| $env:SCCACHE_DIR = "$env:LOCALAPPDATA\Mozilla\sccache" | |
| } | |
| # Quick test to ensure sccache is working | |
| sccache --show-stats | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "Sccache is operational" | |
| echo "sccache_enabled=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append | |
| } else { | |
| Write-Host "::warning::Sccache failed, disabling compile caching" | |
| echo "sccache_enabled=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append | |
| echo "CMAKE_C_COMPILER_LAUNCHER=" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| echo "CMAKE_CXX_COMPILER_LAUNCHER=" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| } | |
| - name: setup environment | |
| shell: powershell | |
| id: set_vars | |
| run: .\scripts\ci-set-vars.ps1 | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - name: "Fetch embedded dependencies" | |
| shell: powershell | |
| run: | | |
| ./scripts/install-deps.ps1 --skip-vcpkg | |
| type ./_deps/sources/CMakeLists.txt | |
| - name: Install Qt | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| # 6.10, not the 6.11 the other platforms pin: Qt restructured its Windows package | |
| # repository at 6.11 into per-architecture subdirectories | |
| # (qt6_6111/qt6_6111_msvc2022_64/Updates.xml) where every release up to 6.10 kept a flat | |
| # one (qt6_6103/Updates.xml). aqtinstall still requests the flat path, so a 6.11 install | |
| # here dies with "Failed to locate XML data for Qt version '6.11.1'". 3.3.0 is its latest | |
| # release, so there is nothing newer to move to; Linux and macOS are unaffected because | |
| # their repositories stayed flat. | |
| # | |
| # 6.10 carries all four modules below, so this costs no feature -- only the configure-time | |
| # notice that the detected Qt is older than the version Contour is tested against. Raise | |
| # this to "6.11.*" once aqtinstall learns the new layout. | |
| version: "6.10.*" | |
| #version: "5.15.*" | |
| modules: qtmultimedia qt5compat qtshadertools qtspeech | |
| - name: "Setup NuGet for vcpkg caching" | |
| shell: bash | |
| run: | | |
| dotnet nuget add source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ | |
| --name GitHub \ | |
| --username "${{ github.repository_owner }}" \ | |
| --password "${{ secrets.GITHUB_TOKEN }}" \ | |
| --store-password-in-clear-text | |
| - name: "Cache vcpkg" | |
| uses: actions/cache@v4 | |
| id: vcpkg-cache | |
| with: | |
| path: ${{ runner.workspace }}/vcpkg | |
| key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} | |
| restore-keys: | | |
| vcpkg-${{ runner.os }}- | |
| - name: "Upgrade NuGet" | |
| run: | | |
| choco upgrade nuget.commandline -y | |
| - name: "vcpkg: Clone and bootstrap" | |
| if: steps.vcpkg-cache.outputs.cache-hit != 'true' | |
| run: | | |
| git clone --depth 1 https://github.com/microsoft/vcpkg.git ${{ runner.workspace }}\vcpkg | |
| ${{ runner.workspace }}\vcpkg\bootstrap-vcpkg.bat | |
| ${{ runner.workspace }}\vcpkg\vcpkg.exe fetch nuget | |
| env: | |
| VCPKG_BINARY_SOURCES: "clear;nuget,GitHub,readwrite" | |
| - name: "vcpkg: Install dependencies" | |
| shell: pwsh | |
| run: | | |
| ${{ runner.workspace }}\vcpkg\vcpkg.exe install --triplet x64-windows | |
| env: | |
| VCPKG_ROOT: "${{ runner.workspace }}/vcpkg" | |
| VCPKG_BINARY_SOURCES: "clear;nuget,GitHub,readwrite" | |
| - name: "Install cmake" | |
| uses: lukka/get-cmake@v3.29.0 | |
| - name: "Check cmake version" | |
| run: cmake --version | |
| - uses: ilammy/msvc-dev-cmd@v1 | |
| - name: "Generate build files" | |
| shell: pwsh | |
| run: | | |
| cmake --preset ${{ env.CMAKE_PRESET }} | |
| env: | |
| VCPKG_ROOT: "${{ runner.workspace }}/vcpkg" | |
| - name: "Build" | |
| shell: pwsh | |
| run: | | |
| cmake --build --preset ${{ env.CMAKE_PRESET }} | |
| - name: "Show sccache statistics" | |
| if: steps.verify_sccache.outputs.sccache_enabled == 'true' | |
| shell: pwsh | |
| run: | | |
| sccache --show-stats | |
| - name: "Test" | |
| shell: pwsh | |
| run: | | |
| ctest --preset ${{ env.CMAKE_PRESET }} --build-config RelWithDebInfo | |
| - name: "verify the AF_UNIX tests actually ran" | |
| # Windows is the ONLY validation the daemon's Windows transport gets -- nobody develops it | |
| # on a Windows box. The AF_UNIX cases self-skip where the platform has no AF_UNIX, and a | |
| # Catch2 SKIP exits 0, so a green suite cannot distinguish "the transport works" from "it | |
| # was never exercised". `windows-latest` is far past the 10 1803 that introduced AF_UNIX, | |
| # so a skip here is a regression, not a platform fact. Same assertion the Linux matrix | |
| # makes about the tmux oracles. | |
| # | |
| # On WIN32 the top-level CMakeLists redirects CMAKE_RUNTIME_OUTPUT_DIRECTORY to | |
| # <binaryDir>/bin, so the executables do NOT sit beside their source directory the way they | |
| # do on Linux. The preset's generator is Ninja (single-config), so there is no | |
| # per-configuration subdirectory under it either. | |
| shell: bash | |
| run: | | |
| set -o pipefail | |
| for binary in \ | |
| "out/build/${CMAKE_PRESET}/bin/net_test.exe" \ | |
| "out/build/${CMAKE_PRESET}/bin/vthost_test.exe"; do | |
| # A missing binary must not read as "nothing skipped": that is the very failure mode | |
| # this step exists to catch, one directory layout further out. | |
| [ -x "$binary" ] || { echo "::error::$binary not found; the assertion below would pass vacuously"; exit 1; } | |
| echo "--- $binary [afunix]" | |
| output=$("$binary" '[afunix]' 2>&1) || { printf '%s\n' "$output"; exit 1; } | |
| printf '%s\n' "$output" | |
| if printf '%s' "$output" | grep -q 'skipped'; then | |
| echo "::error::$binary [afunix] reported SKIPPED; the daemon's AF_UNIX transport is not being exercised on Windows" | |
| exit 1 | |
| fi | |
| done | |
| - name: "Install Wix Toolset" | |
| run: | | |
| # Use this to list all available versions | |
| # dotnet tool search --global wix --detail | |
| # or at URL: https://www.nuget.org/packages/wix#versions-body-tab | |
| dotnet tool install --global wix --version 4.0.6 | |
| echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH | |
| - name: "Create Package(s)" | |
| shell: powershell | |
| run: | | |
| cpack --preset ${{ env.CMAKE_PRESET }} -V | |
| if ($LASTEXITCODE -ne 0) { | |
| echo "CPack failed. Dumping wix.log..." | |
| type "out/build/${{ env.CMAKE_PRESET }}/_CPack_Packages/win64/WIX/wix.log" | |
| exit $LASTEXITCODE | |
| } | |
| - name: "Rename package(s)" | |
| run: | | |
| mv "out\build\${{ env.CMAKE_PRESET }}\Contour-${{ steps.set_vars.outputs.version }}-win64.msi" "contour-${{ steps.set_vars.outputs.version }}-win64.msi" | |
| mv "out\build\${{ env.CMAKE_PRESET }}\Contour-${{ steps.set_vars.outputs.version }}-win64.zip" "contour-${{ steps.set_vars.outputs.version }}-win64.zip" | |
| - name: "Uploading MSI to artifact store" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.version }}-win64.msi" | |
| path: "contour-${{ steps.set_vars.outputs.version }}-win64.msi" | |
| if-no-files-found: error | |
| retention-days: 7 | |
| - name: "Uploading ZIP to artifact store" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.version }}-win64.zip" | |
| path: "contour-${{ steps.set_vars.outputs.version }}-win64.zip" | |
| if-no-files-found: error | |
| retention-days: 7 | |
| # }}} | |
| # {{{ Ubuntu 24.04 CC matrix | |
| ubuntu_2404_cc_matrix: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| cxx: [23] | |
| build_type: ["RelWithDebInfo"] | |
| compiler: | |
| [ | |
| "GCC 14", | |
| "Clang 22", | |
| ] | |
| qt_version: [6] | |
| runner: | |
| [ | |
| "ubuntu-24.04", | |
| "ubuntu-24.04-arm", | |
| ] | |
| name: "${{ matrix.runner }} (${{ matrix.compiler }}, C++${{ matrix.cxx }}, Qt${{ matrix.qt_version }})" | |
| runs-on: ${{ matrix.runner }} | |
| outputs: | |
| id: "${{ matrix.runner }} ${{ matrix.compiler }} (C++${{ matrix.cxx }}, ${{ matrix.build_type }}, ${{ matrix.qt_version }})" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: "ccache-{{ matrix.runner }}-${{ matrix.compiler }}-${{ matrix.cxx }}-${{ matrix.build_type }}-${{ matrix.qt_version }}" | |
| max-size: 256M | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set environment variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - name: "Set up output var: CC_VERSION" | |
| id: extract_matrix | |
| run: | | |
| CC_VERSION=$( echo "${{ matrix.compiler }}" | awk '{ print $2; }') | |
| echo "CC_VERSION=${CC_VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: "Set up QTVER var" | |
| id: set_qtver | |
| run: | | |
| if [[ ${{ matrix.qt_version }} = "5" ]]; then | |
| echo "QTVER=5" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "QTVER=6" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: "install dependencies" | |
| run: sudo env QTVER="${{ matrix.qt_version }}" SYSDEP_ASSUME_YES=ON ./scripts/install-deps.sh | |
| - name: "install tmux (oracle for the tmux-interop tests)" | |
| # Without a tmux binary the tmux-compatibility tests self-skip, which is how they went | |
| # their entire existence without once running: LayoutString's two select-layout oracles, | |
| # TmuxGateway's real `tmux -C` drive, and TmuxController's GUI mirror. They are the only | |
| # coverage `vthost/tmux/ControlModeSpawn.cpp` gets at all. | |
| # | |
| # Built from source rather than installed from the distro, because Ubuntu 24.04 ships tmux | |
| # 3.4 and ImsgServer's "a real tmux binary attaches over imsg" needs the rewritten imsg | |
| # framing that landed after 3.5. With 3.4 that oracle self-skipped, and the verification step | |
| # below had to exclude it by name to keep the matrix green — so the one test covering the | |
| # imsg endpoint never ran anywhere. | |
| # | |
| # 3.7b is not an arbitrary "new enough": it is the version the control-mode server is pinned | |
| # to (see docs/internals/vthost.md), so the oracle now measures us against the exact protocol | |
| # we claim to speak. | |
| run: | | |
| set -ex | |
| # tmux's configure hard-errors without these two; the runner image carries neither the | |
| # libevent nor the ncurses development headers, only their runtimes. | |
| sudo apt -qy install libevent-dev libncurses-dev | |
| curl -sSL -o "$RUNNER_TEMP/tmux.tar.gz" \ | |
| https://github.com/tmux/tmux/releases/download/3.7b/tmux-3.7b.tar.gz | |
| tar xzf "$RUNNER_TEMP/tmux.tar.gz" -C "$RUNNER_TEMP" | |
| # The release tarball ships a generated configure, so no autotools are needed here. | |
| # --disable-utf8proc: see the macOS job for why it must be stated rather than defaulted. | |
| # Passing it here too keeps both platforms measuring against the same tmux build. | |
| cd "$RUNNER_TEMP/tmux-3.7b" \ | |
| && ./configure --disable-utf8proc \ | |
| && make -j"$(nproc)" \ | |
| && sudo make install | |
| tmux -V | |
| - name: "Post-fix embedded dependency permissions." | |
| run: sudo find _deps/sources -exec chown $UID {} \; | |
| # Ubuntu 24.04's distro Qt is 6.4.2, which does not provide the Qt6::GuiPrivate CMake package | |
| # the RHI renderer needs (rhi/qrhi.h). Install a modern Qt6 over it; install-qt-action prepends | |
| # its Qt to CMAKE_PREFIX_PATH via the Qt6_DIR/QT_ROOT_DIR env vars it exports, so find_package | |
| # resolves this one ahead of the distro package. | |
| - name: "Install Qt (GuiPrivate-capable)" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: "6.11.*" | |
| modules: qtmultimedia qt5compat qtshadertools qtspeech | |
| - name: Install GCC | |
| if: ${{ startsWith(matrix.compiler, 'GCC') }} | |
| run: sudo apt install -y g++-${{ steps.extract_matrix.outputs.CC_VERSION }} | |
| - name: Install Clang | |
| if: ${{ startsWith(matrix.compiler, 'Clang') }} | |
| env: | |
| CC_VERSION: ${{ steps.extract_matrix.outputs.CC_VERSION }} | |
| run: | | |
| wget https://apt.llvm.org/llvm.sh | |
| chmod +x llvm.sh | |
| sudo ./llvm.sh "$CC_VERSION" | |
| sudo apt install -y "clang-$CC_VERSION" | |
| sudo apt install -y "clang-tidy-$CC_VERSION" | |
| - name: "create build directory" | |
| run: mkdir build | |
| - name: CMake version | |
| run: cmake --version | |
| - name: "cmake" | |
| run: | | |
| CC_NAME=$(echo "${{ matrix.compiler }}" | awk '{ print tolower($1); }') | |
| CC_VER=$( echo "${{ matrix.compiler }}" | awk '{ print $2; }') | |
| test "${{ matrix.compiler }}" = "GCC 8" && EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DPEDANTIC_COMPILER_WERROR=ON" | |
| test "${CC_NAME}" = "gcc" && CC_EXE="g++" | |
| if [[ "${CC_NAME}" = "clang" ]]; then | |
| CC_EXE="clang++" | |
| # CMAKE_CXX_FLAGS="-stdlib=libc++" | |
| # CMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -lc++abi" | |
| # EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DENABLE_TIDY=ON" | |
| # EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DPEDANTIC_COMPILER_WERROR=OFF" | |
| fi | |
| cmake \ | |
| $EXTRA_CMAKE_FLAGS \ | |
| -DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" \ | |
| -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} \ | |
| -DCMAKE_CXX_COMPILER="${CC_EXE}-${CC_VER}" \ | |
| -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" \ | |
| -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" \ | |
| -DCMAKE_INSTALL_PREFIX="/usr" \ | |
| -DCONTOUR_WAYLAND=OFF \ | |
| -DLIBUNICODE_UCD_BASE_DIR=$PWD/_ucd \ | |
| -DPEDANTIC_COMPILER_WERROR=OFF \ | |
| --preset gcc-debug | |
| - name: "build" | |
| run: cmake --build --preset gcc-debug -- -j3 | |
| - name: "tests" | |
| # A hung test (e.g. a dead-loop) must fail the matrix fast, not stall for hours. | |
| # | |
| # Budget, measured rather than assumed: the offscreen suite is ~2 minutes here. The e2e | |
| # app runs and contour_gui_test dominate it (~20-30s each); the unit suites are all under | |
| # 3s. The two vtconformance gates would add ~3 minutes, but they self-skip on a runner | |
| # that has not fetched vttest/esctest. Keep an eye on this ceiling when adding an e2e run. | |
| timeout-minutes: 5 | |
| run: cmake --build --preset gcc-debug --target test | |
| - name: "verify the tmux oracles actually ran" | |
| # ctest prints nothing for a passing test, and a Catch2 SKIP exits 0 — so "the suite is | |
| # green" cannot distinguish "the oracle agreed with us" from "no tmux, nothing ran". That | |
| # ambiguity is exactly how these tests went their whole existence without executing. Assert | |
| # it instead of inferring it: re-run the tagged subset and fail if anything reports skipped. | |
| # | |
| # Nothing is excluded any more: the job builds tmux 3.7b above, so the imsg oracle -- which | |
| # needs the framing rewritten after 3.5 and had been carved out by name ever since -- runs | |
| # like the rest and is held to the same "did not skip" bar. | |
| run: | | |
| set -o pipefail | |
| for oracle in \ | |
| 'out/gcc-debug/src/vthost/vthost_test|[oracle]' \ | |
| 'out/gcc-debug/src/contour/contour_gui_test|[oracle]'; do | |
| binary=${oracle%%|*} | |
| filter=${oracle##*|} | |
| echo "--- $binary $filter" | |
| output=$("$binary" "$filter" 2>&1) || { printf '%s\n' "$output"; exit 1; } | |
| printf '%s\n' "$output" | |
| if printf '%s' "$output" | grep -q 'skipped'; then | |
| echo "::error::$binary $filter reported SKIPPED; the tmux interop tests are not being exercised" | |
| exit 1 | |
| fi | |
| done | |
| - name: "install weston (headless compositor for display-gated GUI tests)" | |
| # Non-gating until proven on a GitHub runner: the local run is green (headless weston, | |
| # 32 cases), but this step has not yet been validated on ubuntu-24.04 CI, so it must not | |
| # be able to fail the matrix. Drop continue-on-error once a real runner has passed it. | |
| continue-on-error: true | |
| run: sudo apt-get install -qy weston qt6-wayland | |
| - name: "display-gated GUI tests (headless weston)" | |
| # Real-compositor run of the [display] suite (skipped by the offscreen ctest above): a | |
| # private headless weston instance provides a stable Wayland compositor without GPU or X. | |
| continue-on-error: true | |
| run: ./scripts/run-display-tests.sh out/gcc-debug/src/contour/contour_gui_test | |
| - name: "Upload unit tests" | |
| if: ${{ matrix.compiler == 'GCC 14' && matrix.cxx == '23' && matrix.runner == 'ubuntu-24.04'}} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: contour-ubuntu2404-tests | |
| path: | | |
| out/gcc-debug/src/crispy/crispy_test | |
| out/gcc-debug/src/vtparser/vtparser_test | |
| out/gcc-debug/src/vtbackend/vtbackend_test | |
| out/gcc-debug/src/vtbackend/bench-headless | |
| out/gcc-debug/src/vtrasterizer/vtrasterizer_test | |
| out/gcc-debug/src/coro/coro_test | |
| out/gcc-debug/src/net/net_test | |
| out/gcc-debug/src/vtworkspace/vtworkspace_test | |
| out/gcc-debug/src/vthost/vthost_test | |
| out/gcc-debug/src/vtconformance/vtconformance-run | |
| test/images | |
| retention-days: 1 | |
| # }}} | |
| # {{{ Linux AppImage (Using Ubuntu 24.04 as base). | |
| package_for_AppImage: | |
| if: github.ref == 'disabled' | |
| name: "Packaging for AppImage" | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v2 | |
| with: | |
| platforms: all | |
| - name: Set up Docker Buildx | |
| id: buildx | |
| uses: docker/setup-buildx-action@v2 | |
| with: | |
| version: latest | |
| - name: update APT database | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt-get -qy install libxml2-utils | |
| - name: set environment variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - name: build inside docker | |
| run: | | |
| docker buildx build --tag contour-appimage \ | |
| -f .github/appimage/Dockerfile --load . | |
| docker create --name contour-appimage contour-appimage | |
| docker cp contour-appimage:/contour/Contour-latest-x86_64.AppImage contour-${{ steps.set_vars.outputs.VERSION_STRING }}.AppImage | |
| docker container rm contour-appimage | |
| - name: "set mode" | |
| run: sudo chmod +x contour-${{ steps.set_vars.outputs.VERSION_STRING }}.AppImage | |
| - name: "libfuse" | |
| run: sudo apt-get -qy install libfuse2 | |
| - name: "Testing AppImage" | |
| run: ./contour-${{ steps.set_vars.outputs.VERSION_STRING }}.AppImage version | |
| - name: "Uploading AppImage" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}.AppImage" | |
| path: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}.AppImage" | |
| if-no-files-found: error | |
| retention-days: 7 | |
| # }}} | |
| # {{{ Ubuntu 24.04 | |
| package_for_Ubuntu: | |
| name: "Packaging for Ubuntu ${{ matrix.os_version }}" | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os_version: ['26.04'] | |
| runs-on: ubuntu-${{ matrix.os_version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: "ccache-ubuntu-${{ matrix.os_version }}" | |
| max-size: 256M | |
| - name: "install dependencies" | |
| run: sudo env QTVER="${{ steps.set_qtver.outputs.QTVER }}" ./scripts/install-deps.sh | |
| - name: "Post-fix embedded dependency permissions." | |
| run: sudo find _deps/sources -exec chown $UID {} \; | |
| - name: "create build directory" | |
| run: mkdir build | |
| - name: Install CMake | |
| if: matrix.os_version != '24.04' | |
| uses: ssrobins/install-cmake@v1 | |
| - name: CMake version | |
| run: cmake --version | |
| - name: "cmake" | |
| run: | | |
| # TODO: turn on -Werror again, that requires some code changes. | |
| EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS" | |
| EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DCONTOUR_PACKAGE_TERMINFO=OFF" | |
| BUILD_DIR="build" \ | |
| CMAKE_BUILD_TYPE=RelWithDebInfo \ | |
| EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS \ | |
| -DCMAKE_CXX_STANDARD=$CMAKE_CXX_STANDARD \ | |
| -DCMAKE_INSTALL_PREFIX="/usr" \ | |
| -DCONTOUR_TESTING=ON \ | |
| -DCONTOUR_WAYLAND=OFF \ | |
| -DPEDANTIC_COMPILER_WERROR=OFF \ | |
| " \ | |
| ./scripts/ci-prepare-contour.sh | |
| - name: "build" | |
| run: cmake --build --preset gcc-release -- -j3 | |
| - name: "tests" | |
| # Same reasoning as the matrix job's cap: a hung test must fail this job fast rather than | |
| # stall it for hours. Release rather than debug, so the budget is more generous than the | |
| # ~2 minutes measured there. | |
| timeout-minutes: 10 | |
| run: cmake --build --preset gcc-release --target test | |
| - name: "CPack: Creating DEB package" | |
| run: | | |
| set -ex | |
| cpack --preset gcc-release | |
| OS_VERSION=${{ matrix.os_version }} | |
| OS_VERSION=${OS_OVERRIDE/./_} | |
| mv -v "./out/gcc-release/Contour-${{ steps.set_vars.outputs.VERSION_STRING }}-Linux-contour.deb" \ | |
| "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb" | |
| mv -v "./out/gcc-release/Contour-${{ steps.set_vars.outputs.VERSION_STRING }}-Linux-contour-dbgsym.ddeb" \ | |
| "contour-dbgsym-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.ddeb" | |
| - name: "Uploading artifact .deb package" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb" | |
| path: | | |
| contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb | |
| contour-dbgsym-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.ddeb | |
| if-no-files-found: error | |
| retention-days: 7 | |
| - name: "Attempt installing the created .deb" | |
| run: | | |
| sudo dpkg --install "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb" | |
| sudo dpkg --install "contour-dbgsym-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.ddeb" | |
| - name: "Verify installation" | |
| run: | | |
| contour version | |
| # }}} | |
| # {{{ Ubuntu 24.04: test via valgrind | |
| test_ubuntu2404_valgrind: | |
| name: "Run tests via valgrind" | |
| runs-on: ubuntu-24.04 | |
| needs: [ubuntu_2404_cc_matrix] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: | | |
| scripts | |
| - name: "download artifact" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: contour-ubuntu2404-tests | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: "fix unit test permissions" | |
| run: | | |
| find . -name '*_test' -exec chmod 0755 {} \; | |
| find . -name 'bench-headless' -exec chmod 0755 {} \; | |
| - name: "install dependencies" | |
| run: ./scripts/ci-install-run-deps.sh valgrind | |
| - name: "test: crispy (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/crispy/crispy_test | |
| - name: "test: vtparser (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/vtparser/vtparser_test | |
| - name: "test: vtbackend (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/vtbackend/vtbackend_test | |
| - name: "test: vtrasterizer (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/vtrasterizer/vtrasterizer_test | |
| # The daemon's suites. Socket buffers, coroutine frames and fd ownership are precisely the | |
| # code valgrind reads best, and this job is the closest thing the project has to a memory | |
| # gate (no ASan/TSan job exists). | |
| - name: "test: coro (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/coro/coro_test | |
| - name: "test: net (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/net/net_test | |
| - name: "test: vtworkspace (via valgrind)" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/vtworkspace/vtworkspace_test | |
| - name: "test: vthost (via valgrind)" | |
| # ~[oracle]: the tmux interop cases fork a real tmux server over a pty and talk to it on a | |
| # timeout. Under valgrind the child runs at full speed while our side crawls, so those | |
| # budgets expire and the failure says nothing about memory. The Linux matrix runs them | |
| # natively and asserts they ran. | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/vthost/vthost_test '~[oracle]' | |
| # }}} | |
| # {{{ Ubuntu 24.04: VT conformance (esctest gates, vttest is advisory) | |
| test_ubuntu2404_conformance: | |
| name: "Run VT conformance suites" | |
| runs-on: ubuntu-24.04 | |
| needs: [ubuntu_2404_cc_matrix] | |
| steps: | |
| # A full checkout, not a sparse one: the ratchet files the gate is judged against | |
| # (src/vtconformance/test/*.txt) and the golden dumps live in the source tree. | |
| - uses: actions/checkout@v4 | |
| - name: "download artifact" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: contour-ubuntu2404-tests | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: "fix harness permissions" | |
| run: find . -name 'vtconformance-run' -exec chmod 0755 {} \; | |
| - name: "install dependencies" | |
| run: ./scripts/ci-install-run-deps.sh python3 | |
| - name: "install vttest" | |
| run: ./scripts/ci/vttest-install.sh | |
| - name: "fetch esctest" | |
| run: ./scripts/fetch-esctest.sh | |
| # Deliberately WITHOUT --skip-if-missing. The ctest registration keeps that flag, because a | |
| # contributor without the suites installed should not have a red build -- but in CI a suite that | |
| # cannot be found has measured nothing, and must fail rather than quietly report success. That | |
| # silent skip is exactly the hole this job closes: the ratchets existed for months while nothing | |
| # in CI ever ran them. | |
| - name: "conformance: esctest (gating)" | |
| run: | | |
| ./out/gcc-debug/src/vtconformance/vtconformance-run run \ | |
| --suite esctest \ | |
| --suite-dir "$PWD/out/esctest2/esctest" \ | |
| --known-gaps "$PWD/src/vtconformance/test/esctest-known-gaps.txt" \ | |
| --known-failures "$PWD/src/vtconformance/test/esctest-known-failures.txt" \ | |
| --work-dir "$PWD/out/esctest-work" | |
| # vttest gates per scenario (see Scenario::gatesBuild): its self-checking chapters replay a | |
| # recorded command file and are reproducible, so they break the build; its visual chapters are | |
| # still screen-driven and judged against unreviewed goldens, so they report only. | |
| - name: "conformance: vttest" | |
| run: | | |
| ./out/gcc-debug/src/vtconformance/vtconformance-run run \ | |
| --suite vttest \ | |
| --golden-dir "$PWD/src/vtconformance/test/golden" \ | |
| --known-gaps "$PWD/src/vtconformance/test/known-gaps.txt" \ | |
| --command-dir "$PWD/src/vtconformance/test/cmd" \ | |
| --work-dir "$PWD/out/vttest-work" | |
| - name: "upload conformance transcripts" | |
| if: ${{ always() }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: contour-conformance-transcripts | |
| path: | | |
| out/esctest-work | |
| out/vttest-work | |
| retention-days: 3 | |
| # }}} | |
| # {{{ Ubuntu 24.04: Test bench-headless | |
| test_ubuntu2404_bench_headless: | |
| strategy: | |
| matrix: | |
| test_case: | |
| [ | |
| "grid cat", | |
| "grid long" | |
| ] | |
| name: "Run bench-headless" | |
| runs-on: ubuntu-24.04 | |
| needs: [ubuntu_2404_cc_matrix] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: | | |
| scripts | |
| - name: "download artifact" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: contour-ubuntu2404-tests | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: "fix unit test permissions" | |
| run: | | |
| find . -name '*_test' -exec chmod 0755 {} \; | |
| find . -name 'bench-headless' -exec chmod 0755 {} \; | |
| - name: "install dependencies" | |
| run: ./scripts/ci-install-run-deps.sh valgrind | |
| - name: "bench-headless: ${{ matrix.test_case }}" | |
| run: valgrind --error-exitcode=64 ./out/gcc-debug/src/vtbackend/bench-headless ${{ matrix.test_case }} size 1 | |
| # }}} | |
| # {{{ Ubuntu check matrix | |
| check_ubuntu2404_matrix_test_matrix: | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| name: "Ubuntu Linux 24.04 post-check" | |
| needs: | |
| - package_for_Ubuntu | |
| - ubuntu_2404_cc_matrix | |
| - test_ubuntu2404_valgrind | |
| - test_ubuntu2404_bench_headless | |
| - test_ubuntu2404_conformance | |
| steps: | |
| - name: Print matrix status | |
| run: | | |
| echo "Result (CC matrix) : ${{ needs.ubuntu_2404_cc_matrix.result }}" | |
| echo "Result (packaging matrix) : ${{ needs.package_for_Ubuntu.result }}" | |
| echo "Result (conformance) : ${{ needs.test_ubuntu2404_conformance.result }}" | |
| - name: Check build matrix status | |
| if: ${{ needs.ubuntu_2404_cc_matrix.result != 'success' && needs.ubuntu_2404_cc_matrix.result != 'skipped' }} | |
| run: exit 1 | |
| - name: Check package_for_Ubuntu matrix status | |
| if: ${{ needs.package_for_Ubuntu.result != 'success' && needs.package_for_Ubuntu.result != 'skipped' }} | |
| run: exit 1 | |
| - name: Check conformance status | |
| if: ${{ needs.test_ubuntu2404_conformance.result != 'success' && needs.test_ubuntu2404_conformance.result != 'skipped' }} | |
| run: exit 1 | |
| - name: "Delete artifact: contour-ubuntu2404-tests" | |
| uses: geekyeggo/delete-artifact@v5 | |
| with: | |
| name: contour-ubuntu2404-tests | |
| # }}} | |
| # {{{ GUI: test: contour quick shell exit | |
| test_quick_exit: | |
| name: "GUI: Quick Shell Exit" | |
| needs: [package_for_Ubuntu] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os_version: ['26.04'] | |
| runs-on: ubuntu-${{ matrix.os_version }} | |
| env: | |
| # I'm giving up on eliminating all leaks for now. | |
| # There are still some deep inside Qt I can't explain myself if it's because of me. | |
| ASAN_OPTIONS: detect_leaks=0 | |
| # Can be used to execute contour within a certain environment, such as valgrind: | |
| # Valgrind is much more precise, but 10x slower. | |
| CONTOUR_PREFIX: "" # valgrind --leak-check=full --num-callers=64 --error-exitcode=112" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set environment variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb" | |
| - name: "install dependencies" | |
| run: ./scripts/ci/notcurses-install-deps.sh | |
| - name: "install contour" | |
| # `apt-get install ./<file>.deb` (note the ./) resolves and pulls the package's declared runtime | |
| # dependencies (Qt6, libyaml-cpp, …) for this exact release — unlike `dpkg -i`, which fails if any | |
| # dependency is absent. This is why the runtime-deps script no longer hardcodes Qt package names. | |
| run: sudo apt-get install -y "./contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb" | |
| - name: "contour executable test" | |
| run: | | |
| contour version | |
| contour help | |
| - name: "create and patch contour.yml config file" | |
| run: | | |
| set -ex | |
| mkdir -p ~/.config/contour/ | |
| contour generate config to ~/.config/contour/contour.yml | |
| sed -i -e 's/locator: native/locator: mock/' ~/.config/contour/contour.yml | |
| sed -i -e 's/strict_spacing: true/strict_spacing: false/' ~/.config/contour/contour.yml | |
| cat .github/mock-font-locator.yml >> ~/.config/contour/contour.yml | |
| cat ~/.config/contour/contour.yml | |
| - name: "Run Contour: quick exit" | |
| timeout-minutes: 5 | |
| id: Xvfb-contour | |
| run: | | |
| ./scripts/ci/Xvfb-contour-run.sh \ | |
| "quick-exit-dumps/${{ matrix.name }}" \ | |
| true | |
| - name: "Save dump" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: quick-exit-contour-dump | |
| path: quick-exit-dumps | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| - name: "Check result success" | |
| run: | | |
| exit ${{ steps.Xvfb-contour.outputs.exitCode }} | |
| # }}} | |
| # {{{ GUI: external test: notcurses | |
| test_notcurses: | |
| name: "GUI: notcurses-demo ${{ matrix.name }}" | |
| # Must match the OS the consumed .deb targets (26.04): a 26.04-built package links Qt 6.10 and a | |
| # newer libstdc++ than a 24.04 runner provides, so running it there fails with GLIBCXX/Qt-version | |
| # "not found" at load time. | |
| runs-on: ubuntu-26.04 | |
| needs: [package_for_Ubuntu] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: # {{{ (name, id) tuples | |
| # ixetunchmdbkywjgarvlsfqzo | |
| - name: 'aanimate-box-chunli-dragon-eagle-fission-grid-highcon' | |
| id: 'abcdefgh' | |
| - name: 'intro-junle-keller-luigi-mojibake-normal-outro-qrcode' | |
| id: 'ijklmnoq' | |
| - name: 'reel-sliders-trans-uniblock-view-whiteout-xray-yield-zoo' | |
| id: 'rstuvwxyz' | |
| # }}} | |
| env: | |
| LD_LIBRARY_PATH: /home/runner/opt/notcurses/lib | |
| # I'm giving up on eliminating all leaks for now. | |
| # There are still some deep inside Qt I can't explain myself if it's because of me. | |
| ASAN_OPTIONS: detect_leaks=0 | |
| # Can be used to execute contour within a certain environment, such as valgrind: | |
| # Valgrind is much more precise, but 10x slower. | |
| CONTOUR_PREFIX: "" # valgrind --leak-check=full --num-callers=64 --error-exitcode=112" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set environment variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu26.04-amd64.deb" | |
| - name: "install dependencies" | |
| run: ./scripts/ci/notcurses-install-deps.sh | |
| - name: "git clone" | |
| run: git clone https://github.com/dankamongmen/notcurses.git | |
| - name: "configure cmake" | |
| # The smoke test only needs the notcurses-demo binary, so build the minimum: skip the C++ | |
| # bindings and the doctest-based test suite (USE_DOCTEST/BUILD_TESTING — the source of the | |
| # doctest dependency that broke configure), the proof-of-concept binaries (USE_POC), and the | |
| # pandoc man pages. NB: the option is USE_CXX, not USE_CPP — the old -DUSE_CPP=OFF was a no-op, | |
| # which is why the C++/doctest path stayed enabled. | |
| run: | | |
| cmake -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_INSTALL_PREFIX=/home/runner/opt/notcurses \ | |
| -DUSE_CXX=OFF \ | |
| -DUSE_DOCTEST=OFF \ | |
| -DBUILD_TESTING=OFF \ | |
| -DUSE_POC=OFF \ | |
| -DUSE_DEFLATE=OFF \ | |
| -DUSE_MULTIMEDIA=ffmpeg \ | |
| -DUSE_PANDOC=OFF \ | |
| -DUSE_STATIC=ON \ | |
| -S notcurses \ | |
| -B notcurses/build | |
| - name: "Build notcurses" | |
| run: cmake --build notcurses/build/ -- -j3 | |
| - name: "Install notcurses" | |
| run: cmake --install notcurses/build | |
| - name: "Run notcurses-demo -h" | |
| timeout-minutes: 1 | |
| run: LD_LIBRARY_PATH="/home/runner/opt/notcurses/lib" ~/opt/notcurses/bin/notcurses-demo -p ~/opt/notcurses/share/notcurses -h | |
| - name: "install contour" | |
| # See the quick-shell-exit job: apt-get resolves the .deb's declared deps for this release. | |
| run: sudo apt-get install -y "./contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu26.04-amd64.deb" | |
| - name: "contour executable test" | |
| run: | | |
| contour version | |
| contour help | |
| ls -hl ~/opt/notcurses/ | |
| - name: "create and patch contour.yml config file" | |
| run: | | |
| set -ex | |
| mkdir -p ~/.config/contour/ | |
| contour generate config to ~/.config/contour/contour.yml | |
| sed -i -e 's/locator: native/locator: mock/' ~/.config/contour/contour.yml | |
| sed -i -e 's/strict_spacing: true/strict_spacing: false/' ~/.config/contour/contour.yml | |
| cat .github/mock-font-locator.yml >> ~/.config/contour/contour.yml | |
| cat ~/.config/contour/contour.yml | |
| - name: "Run Contour: notcurses-demo ${{ matrix.name }}" | |
| timeout-minutes: 5 | |
| id: Xvfb-contour-notcurses | |
| run: | | |
| ./scripts/ci/Xvfb-contour-run.sh \ | |
| "notcurses-demo-dumps/${{ matrix.name }}" \ | |
| ~/opt/notcurses/bin/notcurses-demo \ | |
| -p ~/opt/notcurses/share/notcurses ${{ matrix.id }} | |
| - name: "Save dump" | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: notcurses-contour-dump | |
| path: notcurses-demo-dumps | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| - name: "Check result success" | |
| run: | | |
| exit ${{ steps.Xvfb-contour-notcurses.outputs.exitCode }} | |
| check_notcurses_test_matrix: | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| name: tests matrix | |
| needs: test_notcurses | |
| steps: | |
| - name: Check build matrix status | |
| if: ${{ needs.test_notcurses.result != 'success' }} | |
| run: exit 1 | |
| # }}} | |
| # {{{ Check Ubuntu package | |
| check_package_ubuntu: | |
| name: "Check packages for Ubuntu " | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os_version: ['26.04'] | |
| runs-on: ubuntu-${{ matrix.os_version}} | |
| needs: [package_for_Ubuntu] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set environment variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version }}-amd64.deb" | |
| - name: "Check install package" | |
| run: | | |
| sudo apt install "./contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu${{ matrix.os_version}}-amd64.deb" | |
| # }}} | |
| # {{{ Release | |
| check_release: | |
| if: github.head_ref == 'release' | |
| name: Check Release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars.sh and check-release.sh | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| - name: run release checks | |
| run: ./scripts/check-release.sh | |
| do_release: | |
| if: github.head_ref == 'release' | |
| name: Create Github release | |
| runs-on: ubuntu-latest | |
| needs: | |
| - check_links | |
| - check_release | |
| - check_notcurses_test_matrix | |
| - macOSArm | |
| - package_for_Ubuntu | |
| - windows | |
| permissions: | |
| # We need write permissions on contents to create GitHub releases and on | |
| # discussions to create the release announcement in the discussion forums | |
| contents: write | |
| discussions: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "update APT database" | |
| run: sudo ./scripts/apt-update.sh | |
| - name: Installing xmllint for ci-set-vars | |
| run: sudo apt -qy install libxml2-utils | |
| - name: set variables | |
| id: set_vars | |
| run: ./scripts/ci-set-vars.sh | |
| env: | |
| REPOSITORY: ${{ github.event.repository.name }} | |
| # {{{ fetch release artifacts | |
| - name: "fetch artifact: Ubuntu 24.04" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: contour-${{ steps.set_vars.outputs.VERSION_STRING }}-ubuntu26.04-amd64.deb | |
| - name: "fetch artifact: MacOS (ARM)" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| # VERSION_STRING, matching the .deb above: `version` omits the `-prerelease` | |
| # suffix, so a prerelease used to publish a .dmg whose name claimed otherwise. | |
| name: "contour-${{ steps.set_vars.outputs.VERSION_STRING }}-macOS-arm.dmg" | |
| - name: "fetch artifact: Windows (MSI)" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.version }}-win64.msi" | |
| - name: "fetch artifact: Windows (ZIP)" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: "contour-${{ steps.set_vars.outputs.version }}-win64.zip" | |
| # - name: "fetch artifact: AppImage" | |
| # uses: actions/download-artifact@v4 | |
| # with: | |
| # name: "contour-${{ steps.set_vars.outputs.version }}.AppImage" | |
| - name: "fetch artifact: Static build" | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: "contour" | |
| # }}} | |
| - name: "inspect" | |
| run: ls -lisahF | |
| - name: inspect recurse | |
| run: pwd; ls -hlaR | |
| # ------------------------------------------------------------- | |
| - name: Create Github release page | |
| id: create_release | |
| uses: softprops/action-gh-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # token provided by Actions | |
| with: | |
| tag_name: v${{ steps.set_vars.outputs.version }}${{ steps.set_vars.outputs.tag_suffix}} | |
| name: Contour ${{ steps.set_vars.outputs.version }} ${{ steps.set_vars.outputs.RELEASENAME_SUFFIX}} | |
| body_path: ${{ github.workspace }}/release-body.md | |
| draft: true | |
| prerelease: ${{ steps.set_vars.outputs.IS_PRERELEASE }} | |
| fail_on_unmatched_files: true | |
| files: | | |
| *.deb | |
| *.ddeb | |
| *.dmg | |
| *.msi | |
| *.zip | |
| # *.AppImage | |
| # }}} | |