Preserve heading anchors in Insert As Relative Link #1118
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: CI-MacOS | |
| on: | |
| push: | |
| branches: [ master ] | |
| pull_request: | |
| branches: [ master ] | |
| # Allows you to run this workflow manually from the Actions tab. | |
| workflow_dispatch: | |
| inputs: | |
| debug_enabled: | |
| type: boolean | |
| description: 'Run the build with tmate debugging enabled' | |
| required: false | |
| default: false | |
| env: | |
| VNOTE_VER: 4.1.1 | |
| CMAKE_VER: 3.24.3 | |
| jobs: | |
| build: | |
| environment: Mac-code-sign | |
| name: Build On MacOS | |
| timeout-minutes: 120 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| config: | |
| - name: "Build on Arm64" | |
| # Qt 6.9.3 includes the QTBUG-137687 fix (FindWrapOpenGL.cmake no | |
| # longer unconditionally links the macOS AGL.framework that Xcode 26 | |
| # removed), so macos-latest is safe again. | |
| os: macos-latest | |
| arch: universal | |
| qt: 6.9.3 | |
| runs-on: ${{matrix.config.os}} | |
| steps: | |
| # Checks-out your repository under $GITHUB_WORKSPACE. | |
| - uses: actions/checkout@v2 | |
| with: | |
| fetch-depth: 1 | |
| submodules: recursive | |
| - name: Init Submodules | |
| run: | | |
| auth_header="$(git config --local --get http.https://github.com/.extraheader)" | |
| git submodule sync --recursive | |
| git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1 | |
| - name: Install Dependencies | |
| run: | | |
| brew install tree libiodbc libpq | |
| - name: Fix SQL | |
| run: | | |
| sudo mkdir -p /usr/local/opt/libiodbc/lib | |
| sudo ln -s /opt/homebrew/opt/libiodbc/lib/libiodbc.2.dylib /usr/local/opt/libiodbc/lib/libiodbc.2.dylib | |
| sudo mkdir -p /Applications/Postgres.app/Contents/Versions/14/lib | |
| sudo ln -s /opt/homebrew/Cellar/libpq/16.3/lib/libpq.5.dylib /Applications/Postgres.app/Contents/Versions/14/lib/libpq.5.dylib | |
| - name: Install a fresh CMake | |
| run: | | |
| wget --no-verbose https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-macos-universal.tar.gz | |
| tar xzf cmake-${CMAKE_VER}-macos-universal.tar.gz | |
| sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack | |
| sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cmake /usr/local/bin/cmake | |
| sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cpack /usr/local/bin/cpack | |
| working-directory: ${{runner.workspace}} | |
| - name: Install macdeployqtfix | |
| run: | | |
| git clone https://github.com/tamlok/macdeployqtfix.git macdeployqtfix --depth=1 | |
| working-directory: ${{runner.workspace}} | |
| - name: Install optool | |
| run: | | |
| wget --no-verbose https://github.com/alexzielenski/optool/releases/download/0.1/optool.zip | |
| unzip ./optool.zip | |
| sudo ln -s ./optool /usr/local/bin/optool | |
| working-directory: ${{runner.workspace}} | |
| - name: Cache Qt | |
| id: cache-qt | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{runner.workspace}}/Qt | |
| key: ${{ runner.os }}-QtCache-6.9 | |
| - name: Install Qt | |
| uses: jurplel/install-qt-action@v3 | |
| with: | |
| version: ${{matrix.config.qt}} | |
| target: desktop | |
| modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport' | |
| cache: 'true' | |
| - name: Create Build Dir | |
| run: mkdir build | |
| working-directory: ${{runner.workspace}} | |
| - name: Configure Project | |
| run: | | |
| qmake -v | |
| cmake --version | |
| cmake -DMACDEPLOYQTFIX_EXECUTABLE=${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" ${GITHUB_WORKSPACE} | |
| working-directory: ${{runner.workspace}}/build | |
| - name: Build Project | |
| run: | | |
| # Keep only required SQL drivers | |
| rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlmimer.dylib | |
| rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlodbc.dylib | |
| rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlpsql.dylib | |
| # Build the project | |
| cmake --build . --target pack | |
| # Fix Qt frameworks | |
| python3 ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py ./src/VNote.app/Contents/MacOS/VNote ${{env.Qt6_DIR}}/../.. | |
| # Only delete rpaths that exist to avoid errors | |
| for rpath in $(otool -l ./src/VNote.app/Contents/MacOS/VNote | awk '/LC_RPATH/ {getline; getline; print $2}' | grep 'vnote'); do | |
| echo "Checking rpath: $rpath" | |
| if otool -l ./src/VNote.app/Contents/MacOS/VNote | grep -q "$rpath"; then | |
| echo "Deleting rpath: $rpath" | |
| install_name_tool -delete_rpath "$rpath" ./src/VNote.app/Contents/MacOS/VNote | |
| else | |
| echo "Rpath not found: $rpath" | |
| fi | |
| done | |
| for rpath in $(otool -l ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | awk '/LC_RPATH/ {getline; getline; print $2}' | grep 'vnote'); do | |
| echo "Checking rpath: $rpath" | |
| if otool -l ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | grep -q "$rpath"; then | |
| echo "Deleting rpath: $rpath" | |
| install_name_tool -delete_rpath "$rpath" ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | |
| else | |
| echo "Rpath not found: $rpath" | |
| fi | |
| done | |
| # Run macdeployqtfix again to ensure all dependencies are properly fixed | |
| python3 ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py ./src/VNote.app/Contents/MacOS/VNote ${{env.Qt6_DIR}}/../.. | |
| working-directory: ${{runner.workspace}}/build | |
| - name: Build and Run Tests | |
| # Build the test executables (tests/CMakeLists.txt narrows them to the | |
| # host arch on macOS, so this links arm64-only against the universal | |
| # libs) then run them headlessly via the Qt offscreen platform plugin. | |
| # VNOTE_BUILD_TESTS defaults to ON, so no extra -D is needed. | |
| # | |
| # Sync/keychain tests are EXCLUDED on macOS. The real qtkeychain path | |
| # (keychain_apple.mm + Security.framework) cannot run in CI: even with a | |
| # created + unlocked default keychain, an UNSIGNED test binary segfaults | |
| # inside Security.framework on a headless runner (verified — making them | |
| # pass would require ad-hoc-codesigning every test exe with keychain | |
| # entitlements). The platform-agnostic sync LOGIC is already covered | |
| # against the real backend on Linux + Windows; the macOS keychain path | |
| # is third-party and only reachable from the signed prod app. | |
| run: | | |
| set -e | |
| cmake --build . | |
| export QT_QPA_PLATFORM=offscreen | |
| # Force a UTF-8 locale so std::filesystem::path round-trips through | |
| # std::string preserve non-ASCII bytes. Without this, CJK filenames | |
| # in tests/data/vnote3_notebooks/* get mangled during VNote3 | |
| # migration (testConvertFolderWith17FilesAndSubfolder, etc.). | |
| # macOS ships en_US.UTF-8 rather than C.UTF-8. | |
| export LC_ALL=en_US.UTF-8 | |
| export LANG=en_US.UTF-8 | |
| # libVTextEdit.dylib lives next to the parent build's VNote binary; | |
| # add it to DYLD_LIBRARY_PATH so Qt-side test exes resolve it at load. | |
| export DYLD_LIBRARY_PATH="${{runner.workspace}}/build/libs/vtextedit/src:${DYLD_LIBRARY_PATH:-}" | |
| ctest --output-on-failure -E '(sync|bootstrap|credential|keychain|syncinfo)' | |
| working-directory: ${{runner.workspace}}/build | |
| - name: Re-run failed tests with extra-verbose output (debug aid) | |
| # On failure of the previous step, re-run only the failing tests with | |
| # --extra-verbose so Qt Test's PASS/FAIL lines + assertion details | |
| # land in the CI log. Diagnostic only; never gates the workflow. | |
| if: failure() | |
| run: | | |
| export QT_QPA_PLATFORM=offscreen | |
| export LC_ALL=en_US.UTF-8 | |
| export LANG=en_US.UTF-8 | |
| export DYLD_LIBRARY_PATH="${{runner.workspace}}/build/libs/vtextedit/src:${DYLD_LIBRARY_PATH:-}" | |
| ctest --rerun-failed --extra-verbose || true | |
| working-directory: ${{runner.workspace}}/build | |
| - name: Codesign Bundle | |
| # Extract the secrets we defined earlier as environment variables | |
| env: | |
| MACOS_CERTIFICATE: ${{ secrets.CLI_MACOS_CERTIFICATE }} | |
| MACOS_CERTIFICATE_PWD: ${{ secrets.CLI_MACOS_CERTIFICATE_PWD }} | |
| MACOS_CERTIFICATE_NAME: ${{ secrets.CLI_MACOS_CERTIFICATE_NAME }} | |
| MACOS_CI_KEYCHAIN_PWD: ${{ secrets.CLI_MACOS_CERTIFICATE }} | |
| run: | | |
| # Turn our base64-encoded certificate back to a regular .p12 file | |
| echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 | |
| # We need to create a new keychain, otherwise using the certificate will prompt | |
| # with a UI dialog asking for the certificate password, which we can't | |
| # use in a headless CI environment | |
| security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
| security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
| echo "Codesigning main app bundle" | |
| codesign --force --deep -s "$MACOS_CERTIFICATE_NAME" --entitlements ${{github.workspace}}/package/entitlements.xml --options runtime ./src/VNote.app | |
| codesign -v -vvv ./src/VNote.app | |
| # Spotlight / XProtect can hold handles on freshly codesigned bundle | |
| # contents, causing `hdiutil create` to fail with "Resource busy". | |
| # Flush filesystem buffers + wait a beat, then retry with backoff. | |
| DMG_NAME="VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg" | |
| sync | |
| sleep 5 | |
| for attempt in 1 2 3 4 5; do | |
| echo "hdiutil create attempt $attempt: $DMG_NAME" | |
| if hdiutil create -volname "VNote" -srcfolder ./src/VNote.app -ov -format UDZO "$DMG_NAME"; then | |
| echo "hdiutil create succeeded on attempt $attempt" | |
| break | |
| fi | |
| echo "hdiutil create failed on attempt $attempt; cleaning up and retrying" | |
| hdiutil detach -force "/Volumes/VNote" 2>/dev/null || true | |
| if [ "$attempt" = "5" ]; then | |
| echo "hdiutil create exhausted retries" | |
| exit 1 | |
| fi | |
| sleep $((attempt * 10)) | |
| done | |
| codesign --force --deep -s "$MACOS_CERTIFICATE_NAME" --entitlements ${{github.workspace}}/package/entitlements.xml --options runtime "./$DMG_NAME" | |
| codesign -v -vvv "./$DMG_NAME" | |
| working-directory: ${{runner.workspace}}/build | |
| # Enable tmate debugging of manually-triggered workflows if the input option was provided | |
| - name: Setup tmate session | |
| uses: mxschmitt/action-tmate@v3 | |
| if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} | |
| - name: Archive DMG | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}} | |
| path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg | |
| - name: Update Tag | |
| if: github.ref == 'refs/heads/master' | |
| run: | | |
| git tag --force continuous-build ${GITHUB_SHA} | |
| git push --force --tags | |
| - name: Update Continuous Build Release | |
| if: github.ref == 'refs/heads/master' | |
| uses: johnwbyrd/update-release@v1.0.0 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg | |
| release: Continuous Build | |
| tag: continuous-build | |
| - name: Release | |
| if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]') | |
| uses: ncipollo/release-action@v1.11.0 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg | |
| commit: master | |
| tag: v${{env.VNOTE_VER}} | |
| allowUpdates: true | |
| draft: true |