Skip to content

refactor: include what these two files use, and reverse by range #3708

refactor: include what these two files use, and reverse by range

refactor: include what these two files use, and reverse by range #3708

Workflow file for this run

name: build-test
on:
push:
# branches only: publishing a release pushes a tag, which is not a change
branches: ['**']
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
# Cache keys, in short: `actions/cache` never overwrites an existing key, so a
# key that stays the same forever is written exactly once and then frozen —
# every later run restores that first snapshot and rebuilds whatever is missing
# from source. Both caches therefore vary their key:
# * conan — keyed on what determines its content (recipes + profile), so it is
# rewritten precisely when the dependencies change.
# * ccache — keyed per run and restored via prefix, so it keeps growing. Only
# the default branch writes it; branch-scoped entries are invisible to other
# branches and would just churn the 10 GB repo quota.
# `CACHE_FLAVOR` separates jobs that resolve a *different* dependency set for the
# same profile — without it they overwrite each other and both keep rebuilding.
# The `*_KEY_SUFFIX` variables stay as a manual "throw it all away" knob.
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1G
CCACHE_KEY_SUFFIX: r1
CONAN_HOME: ${{ github.workspace }}/.conan2
CONAN_KEY_SUFFIX: r1
jobs:
# Builds the core once per profile and, where `bindings` is set, the JNI and
# Python bindings out of the same configure. They used to live in their own
# workflows, which meant compiling the core and resolving its dependencies
# three times over for the same two profiles.
build:
runs-on: ${{ matrix.os }}
env:
CACHE_FLAVOR: main${{ matrix.bindings && '-bindings' || '' }}
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-24.04, build_profile: ubuntu-24.04-clang-18, host_profile: ubuntu-24.04-clang-18, bindings: true }
- { os: ubuntu-24.04, build_profile: ubuntu-24.04-gcc-14, host_profile: ubuntu-24.04-gcc-14 }
- { os: macos-15, build_profile: macos-15-armv8-clang-14, host_profile: macos-15-armv8-clang-14 }
- { os: macos-26, build_profile: macos-26-armv8-clang-14, host_profile: macos-26-armv8-clang-14, bindings: true }
- { os: windows-2022, build_profile: windows-2022-msvc-1940, host_profile: windows-2022-msvc-1940 }
# android is covered by `android.yml`, which cross compiles the core
# *and* the JNI bindings for every ABI the AAR ships and runs them on
# an emulator
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: ubuntu install ccache
if: runner.os == 'Linux'
run: |
sudo apt install ccache
ccache -V
- name: macos install ccache
if: runner.os == 'macOS'
run: |
brew install ccache
ccache -V
- name: setup java
if: matrix.bindings
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4 # v5
with:
distribution: temurin
java-version: 21
- name: setup python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: 3.14
- name: install python dependencies
run: pip install conan ${{ matrix.bindings && 'pytest' || '' }}
- name: conan cache key
shell: bash
run: echo "CONAN_CACHE_KEY=${{ hashFiles('conanfile.py', '.github/config/conan/**') }}" >> "$GITHUB_ENV"
- name: cache conan
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CONAN_HOME }}
key: conan-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CONAN_KEY_SUFFIX }}-${{ env.CONAN_CACHE_KEY }}
restore-keys: |
conan-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CONAN_KEY_SUFFIX }}-
- name: conan config
run: conan config install .github/config/conan
- name: restore ccache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
restore-keys: |
ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CCACHE_KEY_SUFFIX }}-
- name: conan install
run: >
conan install .
${{ matrix.bindings && '-o "&:with_jni=True" -o "&:with_python=True"' || '' }}
--profile:host '${{ matrix.host_profile }}'
--profile:build '${{ matrix.build_profile }}'
--build missing
- name: cmake
if: runner.os != 'Windows'
run: >
cmake -B build -S .
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_CXX_FLAGS="-Werror"
-DCMAKE_INSTALL_PREFIX=install
-DODR_TEST=ON
${{ matrix.bindings && '-DODR_JNI=ON -DODR_PYTHON=ON' || '' }}
- name: cmake
if: runner.os == 'Windows'
run: >
cmake -B build -S .
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX=install
-DODR_TEST=ON
- name: build
run: cmake --build build --config Release
- name: save ccache
if: github.ref == 'refs/heads/main'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
# Both suites run in seconds, so they stay in this job rather than paying
# an artifact round-trip. The heavy `odr_test` still runs in `test`, which
# needs firefox and the reference output — this job compiles `odr_test`
# but never runs it, so it never fetches the data either.
- name: junit
if: matrix.bindings
run: ctest --test-dir build/jni --output-on-failure
- name: pytest
if: matrix.bindings
run: ctest --test-dir build/python --output-on-failure
- name: install
run: cmake --build build --target install --config Release
- name: upload binaries to github
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: bin-${{ matrix.host_profile }}
path: |
install
build/test/odr_test
build/test/Release/odr_test.exe
if-no-files-found: error
include-hidden-files: true
docker:
needs: build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-24.04, host_profile: ubuntu-24.04-clang-18 }
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
- uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with:
# list of Docker images to use as base name for tags
images: |
ghcr.io/${{ github.repository_owner }}/odr_core_cli
# generate Docker tags based on the following events/attributes
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=sha
- name: download binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: bin-${{ matrix.host_profile }}
path: cli
- name: Build and push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: cli
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/odr_core_cli:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/odr_core_cli:buildcache,mode=max
# TODO try to run it
test:
needs: build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# Disabled while pdf2htmlEX was still built; that blocker is gone, but
# the reference output is generated on macOS, so re-enabling needs a
# cross-platform comparison run first.
#- { os: ubuntu-24.04, host_profile: ubuntu-24.04-clang-18 }
#- { os: ubuntu-24.04, host_profile: ubuntu-24.04-gcc-14 }
- { os: macos-26, host_profile: macos-26-armv8-clang-14 }
# Windows test disabled because:
# Running main() from C:\Users\runneradmin\.conan2\p\b\gtestdd9407d368b89\b\src\googletest\src\gtest_main.cc
# [ FATAL ] C:/Users/runneradmin/.conan2/p/gtest28fa6787e7f6e/p/include\gtest/internal/gtest-param-util.h(585):: Condition IsValidParamName(param_name) failed. Parameterized test name 'odr_private\docx\03_smpldap_docx' is invalid, in D:\a\OpenDocument.core\OpenDocument.core\test\src\html_output_test.cpp line 129
#- { os: windows-2022, host_profile: windows-2022-msvc-1940 }
env:
# read by the git credential helper, on every step that runs git
GH_TOKEN: ${{ secrets.PAT_ANDIWAND }}
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
# The test data stopped being submodules (see cmake/test_data.cmake), so
# the PAT is no longer the checkout token — it authenticates the two
# private clones instead. A credential helper rather than a url rewrite,
# so the token never lands in a git config file.
#
# `GH_TOKEN` is on the job rather than this step: the helper
# `gh auth setup-git` installs shells back out to `gh auth git-credential`,
# which reads the token from the environment at the moment git asks it.
# Set only here, the fetch below falls through to prompting and dies with
# "could not read Username for 'https://github.com'".
- name: authenticate to the private test data
run: gh auth setup-git
- name: fetch test data
run: cmake -DODR_TEST_DATA_UPDATE=ON -P cmake/test_data.cmake
- name: ubuntu install tidy
if: runner.os == 'Linux'
run: sudo apt install tidy
- name: macos install tidy
if: runner.os == 'macOS'
run: brew install tidy-html5
- name: set up python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: 3.14
- name: install python dependencies
run: pip install htmlcmp==2.2.0 opentype-sanitizer==9.2.0
- name: download binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: bin-${{ matrix.host_profile }}
path: .
- name: fix artifact permissions
if: runner.os != 'Windows'
run: chmod +x build/test/odr_test
- name: test
if: runner.os != 'Windows'
working-directory: build/test
run: ./odr_test
- name: windows fix artifact permissions
if: runner.os == 'Windows'
run: chmod +x build/test/Release/odr_test.exe
- name: windows test
if: runner.os == 'Windows'
working-directory: build/test
run: ./Release/odr_test.exe
# OTS is what every browser puts in front of an `@font-face`, and a font
# it rejects is dropped whole. Deterministic, needs no browser, and — the
# point — needs no reference output, so regenerating that cannot bury a
# regression here. See #765.
- name: check embedded fonts survive OTS
run: python test/scripts/check_fonts.py build/test/output
- name: tidy public test outputs
run: html-tidy --html-tidy-config test/scripts/html-tidy-config build/test/output/odr-public/output
- name: compare public test outputs
shell: bash
run: |
compare-html \
--driver firefox \
--max-workers 2 \
test/data/reference-output/odr-public/output \
build/test/output/odr-public/output
- name: tidy private test outputs
run: html-tidy --html-tidy-config test/scripts/html-tidy-config build/test/output/odr-private/output
- name: compare private test outputs
shell: bash
run: |
compare-html \
--driver firefox \
--max-workers 2 \
test/data/reference-output/odr-private/output \
build/test/output/odr-private/output
build-test-downstream:
runs-on: ${{ matrix.os }}
# Exports odrcore and builds a consumer against it, resolving through
# `conan.lock`, so its dependency set differs from the `build` job's.
env:
CACHE_FLAVOR: downstream
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-24.04, build_profile: ubuntu-24.04-clang-18, host_profile: ubuntu-24.04-clang-18 }
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: ubuntu install ccache
if: runner.os == 'Linux'
run: |
sudo apt install ccache
ccache -V
- name: macos install ccache
if: runner.os == 'macOS'
run: |
brew install ccache
ccache -V
- name: set up python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: 3.14
- name: install python dependencies
run: pip install conan
- name: conan cache key
shell: bash
run: echo "CONAN_CACHE_KEY=${{ hashFiles('conanfile.py', 'conan.lock', '.github/config/conan/**') }}" >> "$GITHUB_ENV"
- name: cache conan
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CONAN_HOME }}
key: conan-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CONAN_KEY_SUFFIX }}-${{ env.CONAN_CACHE_KEY }}
restore-keys: |
conan-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CONAN_KEY_SUFFIX }}-
- name: conan config
run: conan config install .github/config/conan
- name: conan odrcore
run: conan export . --name odrcore --version 0.0.0
- name: restore ccache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
restore-keys: |
ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CCACHE_KEY_SUFFIX }}-
- name: conan install
run: >
conan install .
--lockfile conan.lock
--profile:host '${{ matrix.host_profile }}'
--profile:build '${{ matrix.build_profile }}'
--output-folder build
--build missing
- name: conan downstream
run: >
conan install test/downstream
--profile:host '${{ matrix.host_profile }}'
--profile:build '${{ matrix.build_profile }}'
--output-folder test/downstream/build
--build missing
- name: cmake
run: >
cmake -S test/downstream -B test/downstream/build
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
-DCMAKE_BUILD_TYPE=Release
- name: build
run: cmake --build test/downstream/build --config Release
- name: save ccache
if: github.ref == 'refs/heads/main'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.host_profile }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
- name: run
if: matrix.build_profile == matrix.host_profile && (runner.os == 'Linux' || runner.os == 'macOS')
run: test/downstream/build/odr-test-downstream
- name: run
if: matrix.build_profile == matrix.host_profile && runner.os == 'Windows'
run: test/downstream/build/Release/odr-test-downstream.exe