Quick view: image viewer has context menu #793
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 | |
| on: | |
| push: | |
| branches-ignore: [gh-pages] # metrics commits to gh-pages must not re-trigger CI | |
| tags: ['**'] # but keep tag pushes so the create-release job still runs | |
| pull_request: | |
| workflow_dispatch: | |
| # github-action-benchmark pushes the metrics history to the gh-pages branch. | |
| permissions: | |
| contents: write | |
| deployments: write | |
| pull-requests: read # dorny/paths-filter reads the changed-files list on PRs | |
| env: | |
| # Path to the solution file relative to the root of the project. | |
| SOLUTION_FILE_PATH: . | |
| # Configuration type to build. | |
| # You can convert this to a build matrix if you need coverage of multiple configuration types. | |
| # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix | |
| BUILD_CONFIGURATION: Release | |
| jobs: | |
| changes: | |
| name: Detect test-relevant changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| # A manual (workflow_dispatch) run always counts as test-relevant; otherwise defer to the path filter. | |
| tests_relevant: ${{ steps.filter.outputs.tests_relevant == 'true' || github.event_name == 'workflow_dispatch' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Detect test-relevant changes | |
| uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| tests_relevant: | |
| - 'file-commander-core/src/**' | |
| - 'file-commander-core/core-tests/**' | |
| - 'qt-app/src/**' # compiled by the gui-tests target (typed prompt, dialog, launch routing) | |
| - 'qt-app/gui-tests/**' | |
| - 'thin_io/**' | |
| - '.github/workflows/CI.yml' | |
| - 'global.pri' | |
| - 'file-commander-core/config.pri' | |
| build: | |
| name: Build app & installer on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-22.04, macos-14, windows-latest] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Sys info | |
| if: "startsWith(matrix.os, 'mac')" | |
| run: | | |
| sysctl -a | grep cpu | grep hw | |
| sysctl -a | grep brand_string | |
| sysctl -n hw.machine | |
| - name: Sys info | |
| if: "startsWith(matrix.os, 'ubuntu')" | |
| run: | | |
| lscpu | |
| - name: LOC count - app | |
| if: "startsWith(matrix.os, 'windows')" | |
| shell: pwsh | |
| run: | | |
| winget install --id AlDanial.Cloc --exact --silent --accept-package-agreements --accept-source-agreements | |
| "$env:LOCALAPPDATA\Microsoft\WinGet\Links" >> $env:GITHUB_PATH | |
| $loc = (cloc --include-lang="C,C++,C/C++ Header" --strip-str-comments --exclude-dir=tests,bin,build,.git,3rdparty,trigramfrequencytables,gui-tests,core-tests --json --quiet ./qt-app/ ./plugins/ ./file-commander-core/ | ConvertFrom-Json).SUM.code | |
| "App C/C++ code: $loc LOC" | |
| "LOC_APP=$loc" >> $env:GITHUB_ENV | |
| - name: LOC count - libs | |
| if: "startsWith(matrix.os, 'windows')" | |
| shell: pwsh | |
| run: | | |
| $loc = (cloc --include-lang="C,C++,C/C++ Header" --strip-str-comments --exclude-dir=tests,bin,build,.git,3rdparty,trigramfrequencytables,qt-app,plugins,file-commander-core --json --quiet . | ConvertFrom-Json).SUM.code | |
| "Libs C/C++ code: $loc LOC" | |
| "LOC_LIBS=$loc" >> $env:GITHUB_ENV | |
| - name: Install Qt | |
| if: "startsWith(matrix.os, 'windows')" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.10.*' | |
| target: desktop | |
| arch: 'win64_msvc2022_64' | |
| archives: 'qtbase icu qtsvg' | |
| modules: 'qt5compat qtimageformats' | |
| cache: 'true' | |
| - name: Install Qt | |
| if: "startsWith(matrix.os, 'ubuntu')" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.10.*' | |
| target: desktop | |
| arch: 'linux_gcc_64' | |
| archives: 'qtbase icu qtsvg' | |
| modules: 'qt5compat qtimageformats' | |
| cache: 'true' | |
| - name: Install Qt | |
| if: "startsWith(matrix.os, 'macos')" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.10.*' | |
| target: desktop | |
| arch: 'clang_64' | |
| archives: 'qtbase icu qtsvg' | |
| modules: 'qt5compat qtimageformats' | |
| cache: 'true' | |
| - name: Setup MSVC Developer Command Prompt | |
| if: startsWith(matrix.os, 'windows') | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| - name: Windows - building main installer | |
| if: startsWith(matrix.os, 'windows') | |
| shell: cmd | |
| run: | | |
| %QT_ROOT_DIR%\bin\qmake.exe -v | |
| set QTDIR64=%QT_ROOT_DIR% | |
| cd installer\windows\ | |
| create_installer.bat | |
| cd ..\.. | |
| xcopy /R /Y .\installer\windows\binaries\64\msvcr\* .\bin\release\ | |
| xcopy /R /Y .\installer\windows\binaries\64\Qt\* .\bin\release\ | |
| - name: Mac | |
| if: startsWith(matrix.os, 'macos') | |
| working-directory: ${{env.GITHUB_WORKSPACE}} | |
| run: | | |
| clang --version | |
| ./installer/mac/create_dmg.sh $QT_ROOT_DIR | |
| - name: Linux | |
| if: startsWith(matrix.os, 'ubuntu') | |
| working-directory: ${{env.GITHUB_WORKSPACE}} | |
| run: | | |
| export QT_DEBUG_PLUGINS=1 | |
| sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test # jammy ships nothing newer than gcc-12 | |
| sudo apt update -y; sudo apt install -qq gcc-14 g++-14 libglu1-mesa-dev libx11-xcb-dev '^libxcb*' '^libxkb*' libfuse2 | |
| sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50 | |
| sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 50 | |
| g++ --version | |
| export PATH=$QT_ROOT_DIR/bin/:$PATH | |
| wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/7/linuxdeployqt-7-x86_64.AppImage" | |
| chmod a+x linuxdeployqt-7-x86_64.AppImage | |
| unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH | |
| $QT_ROOT_DIR/bin/qmake -r CONFIG+=release | |
| make -j$(nproc) | |
| # Linux: building AppImage | |
| cp ./qt-app/resources/icon.png ./bin/release/ | |
| cp ./installer/linux/file_commander.desktop ./bin/release/ | |
| export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${QT_ROOT_DIR}/lib/ | |
| sudo ldconfig | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/FileCommander -appimage -unsupported-allow-new-glibc -bundle-non-qt-libs -qmake=$QT_ROOT_DIR/bin/qmake -extra-plugins=platforms/,iconengines/,imageformats/,tls/,networkinformation/,platformthemes/,wayland-decoration-client/,wayland-graphics-integration-client/,wayland-shell-integration -executable=./bin/release/libplugin_filecomparison.so.1.0.0 -executable=./bin/release/libplugin_imageviewer.so.1.0.0 -executable=./bin/release/libplugin_textviewer.so.1.0.0 | |
| ls -Ac | |
| mv ./File_Commander*.AppImage ./FileCommander.AppImage | |
| - name: Binary sizes - Windows | |
| if: startsWith(matrix.os, 'windows') | |
| shell: pwsh | |
| run: | | |
| # Sizes of our own binaries (app + every plugin); the Qt/MSVC runtime DLLs xcopied into bin/ are excluded by name. | |
| $exe = Get-Item "bin\release\FileCommander.exe" | |
| foreach ($b in @($exe) + (Get-ChildItem "bin\release\plugin_*.dll")) { | |
| "{0,10:N2} KB {1}" -f ($b.Length / 1KB), $b.Name | |
| } | |
| # Per-PE-section on-disk sizes from the "size of raw data" field of each SECTION HEADER. | |
| # dumpbin is on PATH via the earlier MSVC dev-cmd step. | |
| $name = $null | |
| $sections = @() | |
| foreach ($line in (dumpbin /headers $exe.FullName)) { | |
| if ($line -match '^\s+(\S+) name$') { $name = $Matches[1] } | |
| elseif ($line -match '^\s+([0-9A-Fa-f]+) size of raw data$' -and $name) { | |
| $sections += [pscustomobject]@{ Name = $name; KB = [math]::Round([Convert]::ToInt64($Matches[1], 16) / 1KB, 2) } | |
| $name = $null | |
| } | |
| } | |
| $sections = $sections | Sort-Object KB -Descending | |
| $sections | Format-Table Name, KB -AutoSize | |
| "EXE_KB=$([math]::Round($exe.Length / 1KB, 2))" >> $env:GITHUB_ENV | |
| "IMAGEVIEWER_KB=$([math]::Round((Get-Item 'bin\release\plugin_imageviewer.dll').Length / 1KB, 2))" >> $env:GITHUB_ENV | |
| "TEXTVIEWER_KB=$([math]::Round((Get-Item 'bin\release\plugin_textviewer.dll').Length / 1KB, 2))" >> $env:GITHUB_ENV | |
| # Per-section charts cover the main executable only. | |
| "SECTIONS_JSON=$($sections | ConvertTo-Json -Compress -AsArray)" >> $env:GITHUB_ENV | |
| # Record LOC, exe size + per-section sizes to the gh-pages history and regenerate the chart. | |
| # Only on pushes to master so PRs/forks don't write history (and forks lack a write token). | |
| - name: Assemble metrics data | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/master' && startsWith(matrix.os, 'windows') | |
| shell: pwsh | |
| run: | | |
| $data = @( | |
| @{ name = 'Lines of code (app)'; unit = 'LOC'; value = [int]$env:LOC_APP } | |
| @{ name = 'Lines of code (libs)'; unit = 'LOC'; value = [int]$env:LOC_LIBS } | |
| @{ name = 'FileCommander.exe size'; unit = 'KB'; value = [double]$env:EXE_KB } | |
| @{ name = 'plugin_imageviewer.dll size'; unit = 'KB'; value = [double]$env:IMAGEVIEWER_KB } | |
| @{ name = 'plugin_textviewer.dll size'; unit = 'KB'; value = [double]$env:TEXTVIEWER_KB } | |
| ) | |
| # FileCommander.exe's PE sections, measured by the earlier size step. Each becomes its own chart; .msvcjmc is skipped. | |
| foreach ($s in ($env:SECTIONS_JSON | ConvertFrom-Json) | Where-Object Name -ne '.msvcjmc') { | |
| $data += @{ name = "Section $($s.Name)"; unit = 'KB'; value = $s.KB } | |
| } | |
| $data | ConvertTo-Json -AsArray | Out-File -FilePath metrics.json -Encoding utf8 | |
| Get-Content metrics.json | |
| - name: Update metrics history & chart (gh-pages) | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/master' && startsWith(matrix.os, 'windows') | |
| uses: benchmark-action/github-action-benchmark@v1 | |
| with: | |
| name: File Commander metrics | |
| tool: customSmallerIsBetter | |
| output-file-path: metrics.json | |
| benchmark-data-dir-path: dev/metrics # where data.js + index.html live on gh-pages (default: dev/bench) | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| auto-push: true | |
| # Don't treat a change as a regression | |
| comment-on-alert: false | |
| fail-on-alert: false | |
| # Sizes of our own binaries (app + every plugin), excluding the Qt/system libs the deploy tools copy into bin/. | |
| - name: Binary sizes - Mac | |
| if: startsWith(matrix.os, 'macos') | |
| run: | | |
| cd bin/release | |
| for f in FileCommander.app/Contents/MacOS/FileCommander libplugin_*.dylib; do | |
| [ -f "$f" ] && [ ! -L "$f" ] || continue | |
| awk -v b="$(wc -c < "$f")" -v n="$f" 'BEGIN { printf "%10.2f KB %s\n", b/1024, n }' | |
| done | |
| - name: Binary sizes - Linux | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| cd bin/release | |
| for f in FileCommander libplugin_*.so.*; do | |
| [ -f "$f" ] && [ ! -L "$f" ] || continue | |
| awk -v b="$(wc -c < "$f")" -v n="$f" 'BEGIN { printf "%10.2f KB %s\n", b/1024, n }' | |
| done | |
| - name: Test launch | |
| if: startsWith(matrix.os, 'macos') | |
| run: ./bin/release/FileCommander.app/Contents/MacOS/FileCommander --test-launch | |
| - name: Test launch | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| xvfb-run --auto-servernum ./bin/release/FileCommander --test-launch | |
| - name: Test launch | |
| if: startsWith(matrix.os, 'windows') | |
| run: | | |
| .\bin\release\FileCommander.exe --test-launch | |
| - name: Archive DMG | |
| if: success() && startsWith(matrix.os, 'macos') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: FileCommander.dmg | |
| path: FileCommander.dmg | |
| - name: Archive AppImage | |
| if: success() && startsWith(matrix.os, 'ubuntu') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: FileCommander.AppImage | |
| path: FileCommander.AppImage | |
| - name: Archive Installer | |
| if: success() && startsWith(matrix.os, 'windows') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: FileCommander.exe | |
| path: installer/windows/FileCommander.exe | |
| test: | |
| name: Build & run tests on ${{ matrix.os }} | |
| needs: changes | |
| if: needs.changes.outputs.tests_relevant == 'true' || startsWith(github.ref, 'refs/tags/') | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-22.04, macos-14, windows-latest] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install Qt | |
| if: "startsWith(matrix.os, 'windows')" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.10.*' | |
| target: desktop | |
| arch: 'win64_msvc2022_64' | |
| archives: 'qtbase icu qtsvg' | |
| modules: 'qt5compat qtimageformats' | |
| cache: 'true' | |
| - name: Install Qt | |
| if: "startsWith(matrix.os, 'ubuntu')" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.10.*' | |
| target: desktop | |
| arch: 'linux_gcc_64' | |
| archives: 'qtbase icu qtsvg' | |
| modules: 'qt5compat qtimageformats' | |
| cache: 'true' | |
| - name: Install Qt | |
| if: "startsWith(matrix.os, 'macos')" | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.10.*' | |
| target: desktop | |
| arch: 'clang_64' | |
| archives: 'qtbase icu qtsvg' | |
| modules: 'qt5compat qtimageformats' | |
| cache: 'true' | |
| - name: Setup MSVC Developer Command Prompt | |
| if: startsWith(matrix.os, 'windows') | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| - name: Linux - install build & deploy dependencies | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test # jammy ships nothing newer than gcc-12 | |
| sudo apt update -y; sudo apt install -qq gcc-14 g++-14 libglu1-mesa-dev libx11-xcb-dev '^libxcb*' '^libxkb*' libfuse2 | |
| sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50 | |
| sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 50 | |
| g++ --version | |
| wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/7/linuxdeployqt-7-x86_64.AppImage" | |
| chmod a+x linuxdeployqt-7-x86_64.AppImage | |
| - name: Building tests - Windows | |
| if: startsWith(matrix.os, 'windows') | |
| working-directory: ${{env.GITHUB_WORKSPACE}} | |
| shell: cmd | |
| run: | | |
| cd file-commander-core\core-tests\ | |
| dir | |
| "%QT_ROOT_DIR%\bin\qmake.exe" -tp vc -r | |
| msbuild /t:Build /p:Configuration=Release;PlatformToolset=v143 core-tests.sln | |
| - name: Setup RAM Disk | |
| if: startsWith(matrix.os, 'windows') | |
| uses: chad-golden/setup-ramdisk@v1.0.1 | |
| with: | |
| size-in-mb: 512 # Optional: Default is 2048 | |
| drive-letter: 'R' # Optional: Default is R | |
| copy-workspace: false # Optional: Default is false | |
| - name: Second RAM disk and temp folders | |
| if: startsWith(matrix.os, 'windows') | |
| shell: pwsh | |
| run: | | |
| # Fail the step if imdisk does (pwsh otherwise propagates only the last command's exit code) | |
| $PSNativeCommandUseErrorActionPreference = $true | |
| $ErrorActionPreference = 'Stop' | |
| # The action above leaves ImDisk in System32, so a second RAM disk is one command. S: has to be a | |
| # volume genuinely distinct from R: - that is the whole point of the cross-volume tests. | |
| imdisk -a -s 128M -m S: -p "/FS:NTFS /Q /Y" | |
| # R: is created by the third-party action; format it here so its filesystem is ours to state rather than | |
| # to inherit. Wipes the disk, which is only safe while the action's copy-workspace input is false. | |
| # format.com, not Format-Volume: ImDisk volumes are absent from the Storage provider the cmdlets query. | |
| format.com R: /FS:NTFS /Q /Y /V:RAMDISK | |
| # imdisk delegates -p to format.com and does not report its failure, so assert the end state on both disks: | |
| # anything but NTFS silently drops the reparse-point coverage that needs it. DriveInfo asks the filesystem | |
| # directly, for the same reason format.com is used above. | |
| foreach ($letter in 'R', 'S') { | |
| $filesystem = [System.IO.DriveInfo]::new("${letter}:").DriveFormat | |
| if ($filesystem -ne 'NTFS') { throw "${letter}: is $filesystem, expected NTFS" } | |
| } | |
| New-Item -Path R:\Temp -ItemType Directory -Force | |
| New-Item -Path S:\Temp -ItemType Directory -Force | |
| echo "TEMP=R:\\Temp" >> $env:GITHUB_ENV | |
| echo "TMP=R:\\Temp" >> $env:GITHUB_ENV | |
| echo "TMPDIR=R:\\Temp" >> $env:GITHUB_ENV | |
| echo "FILE_COMMANDER_TEST_SECOND_VOLUME=S:\\Temp" >> $env:GITHUB_ENV | |
| # The runner is elevated, so symlink creation is available; setting this turns a failure to create one | |
| # into a test failure instead of a skip. | |
| echo "FILE_COMMANDER_TEST_SYMLINKS=1" >> $env:GITHUB_ENV | |
| - name: Running tests - Windows | |
| if: startsWith(matrix.os, 'windows') | |
| shell: pwsh | |
| run: | | |
| # Fail the step on the first test executable that exits with a nonzero code (default pwsh behavior only propagates the last command's exit code) | |
| $PSNativeCommandUseErrorActionPreference = $true | |
| $ErrorActionPreference = 'Stop' | |
| .\bin\release\fso_test.exe | |
| .\bin\release\fso_test_high_level.exe | |
| .\bin\release\panel_test.exe | |
| .\bin\release\filesearchengine_test.exe | |
| $N=20 | |
| For ($i=1; $i -le $N; $i++) { | |
| Write-Output "Iteration $i/$N" | |
| $randomSeed = Get-Date -UFormat %s | |
| & .\bin\release\fileoperations_test.exe --std-seed $randomSeed; | |
| } | |
| $randomSeed = Get-Date -UFormat %s | |
| & .\bin\release\filecomparator_test.exe --std-seed $randomSeed; | |
| & .\bin\release\fileoperations_gui_test.exe | |
| - name: Building tests - Mac | |
| if: startsWith(matrix.os, 'macos') | |
| run: | | |
| cd file-commander-core/core-tests | |
| $QT_ROOT_DIR/bin/qmake -r CONFIG+=release | |
| make -j$(sysctl -n hw.ncpu) | |
| cd ../../ | |
| for f in $(find bin/release/ -name *.app); do $QT_ROOT_DIR/bin/macdeployqt $f; done | |
| - name: Case-sensitive second volume for filesystem tests - Mac | |
| if: startsWith(matrix.os, 'macos') | |
| run: | | |
| set -e | |
| # TMPDIR is on the system volume, so this RAM disk is genuinely different. HFSX also gives the | |
| # rename tests a case-sensitive volume without provisioning and attaching another disk. | |
| disk=$(hdiutil attach -nomount ram://262144 | awk '{print $1}') # 512-byte sectors: 128 MB | |
| diskutil erasevolume HFSX fc-second "$disk" | |
| echo "FILE_COMMANDER_TEST_SECOND_VOLUME=/Volumes/fc-second" >> $GITHUB_ENV | |
| echo "FILE_COMMANDER_TEST_CASE_SENSITIVE_VOLUME=/Volumes/fc-second" >> $GITHUB_ENV | |
| - name: Running tests - Mac | |
| if: startsWith(matrix.os, 'macos') | |
| run: | | |
| set -e; ./bin/release/fso_test.app/Contents/MacOS/fso_test | |
| set -e; ./bin/release/fso_test_high_level.app/Contents/MacOS/fso_test_high_level | |
| set -e; ./bin/release/panel_test.app/Contents/MacOS/panel_test | |
| set -e; ./bin/release/filesearchengine_test.app/Contents/MacOS/filesearchengine_test | |
| set -e; for i in `seq 1 20`; do echo "Iteration $i"; ./bin/release/fileoperations_test.app/Contents/MacOS/fileoperations_test --std-seed $(date +%s); sleep 1; done | |
| set -e; ./bin/release/filecomparator_test.app/Contents/MacOS/filecomparator_test --std-seed $(date +%s) | |
| set -e; ./bin/release/fileoperations_gui_test.app/Contents/MacOS/fileoperations_gui_test | |
| - name: Building tests - Linux | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| cd file-commander-core/core-tests | |
| $QT_ROOT_DIR/bin/qmake -r CONFIG+=release | |
| make -j$(nproc) | |
| cd ../../ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/fso_test -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/fso_test_high_level -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/panel_test -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/filesearchengine_test -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/fileoperations_test -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/filecomparator_test -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| ./linuxdeployqt-7-x86_64.AppImage ./bin/release/fileoperations_gui_test -unsupported-allow-new-glibc -bundle-non-qt-libs -extra-plugins=platforms/ | |
| - name: Second volume for cross-volume tests - Linux | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| set -e | |
| # /tmp shares the root filesystem here, so a separate tmpfs mount is what makes a rename into the | |
| # destination genuinely report EXDEV. | |
| sudo mkdir -p /mnt/fc-second | |
| sudo mount -t tmpfs -o size=128M tmpfs /mnt/fc-second | |
| sudo chown "$(id -u):$(id -g)" /mnt/fc-second | |
| echo "FILE_COMMANDER_TEST_SECOND_VOLUME=/mnt/fc-second" >> $GITHUB_ENV | |
| - name: Running tests - Linux | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| set -e; ./bin/release/fso_test | |
| set -e; ./bin/release/fso_test_high_level | |
| set -e; ./bin/release/panel_test | |
| set -e; ./bin/release/filesearchengine_test | |
| set -e; for i in `seq 1 20`; do echo "Iteration $i"; ./bin/release/fileoperations_test --std-seed $(date +%s); sleep 1; done | |
| set -e; ./bin/release/filecomparator_test --std-seed $(date +%s) | |
| set -e; QT_QPA_PLATFORM=offscreen ./bin/release/fileoperations_gui_test | |
| create-release: | |
| needs: [build, test] | |
| if: success() && startsWith(github.ref, 'refs/tags/') | |
| permissions: | |
| contents: write | |
| deployments: write | |
| pull-requests: read | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # required to access full history and tags | |
| fetch-tags: true | |
| - name: Download EXE | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: FileCommander.exe | |
| path: . | |
| - name: Download DMG | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: FileCommander.dmg | |
| path: . | |
| - name: Download AppImage | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: FileCommander.AppImage | |
| path: . | |
| - name: Generate changelog (commits between previous tag and this tag) | |
| id: changelog | |
| shell: pwsh | |
| env: | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| CURRENT_REF: ${{ github.ref }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| # Extract current tag name (strip refs/tags/) | |
| $currentTag = $env:CURRENT_REF -replace '^refs/tags/', '' | |
| # Ensure tags are present | |
| git fetch --tags --prune | |
| # Find previous tag (newest first, excluding current tag) | |
| $tags = git tag --sort=-creatordate | |
| $prevTag = $tags | Where-Object { $_ -ne $currentTag } | Select-Object -First 1 | |
| if ([string]::IsNullOrEmpty($prevTag)) { | |
| $title = "Changes up to $currentTag" | |
| $commits = git log ` | |
| $currentTag ` | |
| --pretty=format:"- %s ([%h]($($env:GITHUB_SERVER_URL)/$($env:GITHUB_REPOSITORY)/commit/%H))" | |
| } | |
| else { | |
| # Resolve the commit the previous tag points to and exclude it explicitly | |
| $prevCommit = git rev-list -n 1 $prevTag | |
| $commits = git log ` | |
| $currentTag ` | |
| --not $prevCommit ` | |
| --pretty=format:"- %s ([%h]($($env:GITHUB_SERVER_URL)/$($env:GITHUB_REPOSITORY)/commit/%H))" | |
| } | |
| if ([string]::IsNullOrWhiteSpace($commits)) { | |
| $commits = "(no commits)" | |
| } | |
| # Write multiline output for softprops/action-gh-release | |
| "body<<EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 | |
| $commits | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 | |
| "EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| if: success() && startsWith(github.ref, 'refs/tags/') | |
| with: | |
| files: | | |
| FileCommander.exe | |
| FileCommander.dmg | |
| FileCommander.AppImage | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| tag_name: ${{ github.ref }} | |
| name: ${{ github.ref_name }} | |
| body: ${{ steps.changelog.outputs.body }} |