Merge remote-tracking branch 'upstream/master' into copying-fix

# Conflicts:
#	sources/diagramevent/diagrameventaddpaste.cpp
This commit is contained in:
Kellermorph
2026-09-17 17:59:12 +02:00
3 changed files with 100 additions and 9 deletions
+79
View File
@@ -0,0 +1,79 @@
# Linux build, unit tests, and the IPC open-forwarding regression gate.
#
# The existing workflows build Windows and generate documentation; nothing
# builds QElectroTech on Linux or runs tests/ in CI. This does both.
#
# It runs inside an ubuntu:26.04 container rather than directly on the runner
# because the IPC gate below needs Qt 6.10.2. The ubuntu-latest image ships
# Qt 6.4, and whether the crash that gate covers reproduces on 6.4 has never
# been checked -- a gate that cannot go red is worse than no gate.
name: Linux build and tests
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
jobs:
linux:
name: Build and test (Qt 6, Debug)
runs-on: ubuntu-latest
container: ubuntu:26.04
steps:
- name: Install build and test dependencies
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates build-essential cmake ninja-build git pkg-config \
qt6-base-dev qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \
libqt6svg6-dev libqt6sql6-sqlite libsqlite3-dev libcups2-dev \
libxkbcommon-x11-0 \
xvfb openbox xdotool x11-utils
# extra-cmake-modules and the KF6 libraries are installed rather than
# left to FetchContent: without them CMake builds extra-cmake-modules
# from source, and its Qt Help documentation target then demands
# Qt6{Core,Gui,Widgets,DBus}Tools, failing the configure with an error
# that appears to be about QElectroTech and is not.
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
extra-cmake-modules libkf6coreaddons-dev libkf6widgetsaddons-dev \
qt6-svg-plugins
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Configure
# Debug, not Release. Whether a use-after-free faults depends on what
# the allocator does with the freed block: the commit before #868
# crashes every time built Debug and survives every time built
# -O3 -DNDEBUG, so a Release job would never catch a regression here.
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DQT_VERSION_MAJOR=6
- name: Build
run: cmake --build build --parallel $(nproc)
- name: Unit tests
run: xvfb-run -a ctest --test-dir build --output-on-failure
- name: IPC open-forwarding regression (#868)
# Fails the job only on an actual crash. An inconclusive run means the
# crash path was not exercised -- it proves nothing, which is not the
# same as a regression, and failing on it would make this job flaky
# rather than informative.
run: |
set +e
tests/ipc-regression/run.sh --binary build/qelectrotech
status=$?
set -e
case $status in
0) echo "::notice::IPC gate passed" ;;
2) echo "::warning::IPC gate inconclusive -- the crash path was not exercised, nothing was proven" ;;
*) echo "::error::IPC gate failed -- forwarded files are being opened inside the socket handler again"
exit 1 ;;
esac
+6 -2
View File
@@ -42,6 +42,9 @@ jobs:
pages: write
id-token: write # Required by SignPath
env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
outputs:
qt6_msi: ${{ steps.export.outputs.msi_name_qt6 }}
@@ -290,7 +293,8 @@ jobs:
# PR/run must never attempt a signing request.
# (cf. DieterMayerOSS:fix/msi-signing-fork-guard, d3f60c88)
- name: Sign MSI via SignPath
if: github.repository == 'qelectrotech/qelectrotech-source-mirror'
id: sign
if: github.repository == 'qelectrotech/qelectrotech-source-mirror' && env.SIGNPATH_API_TOKEN != ''
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
@@ -345,7 +349,7 @@ jobs:
run: |
Write-Host "=== MSI build summary (${{ matrix.flavor }}) ==="
Write-Host "Version : ${{ steps.version.outputs.VERSION_DISPLAY }}"
Write-Host "Signed : true"
Write-Host "Signed : ${{ steps.sign.outcome == 'success' }}"
# ---------------------------------------------------------------------------
# Job 2 : Génère et déploie la page GitHub Pages une fois le MSI publié,
+15 -7
View File
@@ -58,22 +58,30 @@
const QList<QGraphicsItem *> movable = m_content.items(MovableItems);
if (movable.isEmpty()) return;
//Compute the bounding rect centre — this is where the cursor
//will start. Items stay at their original XML positions;
//Compute the top-left of all items' positions (not bounding
//rects) and snap to grid — used only for the initial cursor
//warp. Items stay at their original XML positions;
//moveTo() handles grid-snapped movement via deltas.
QRectF group_rect;
QPointF top_left;
bool first = true;
for (auto *item : movable) {
group_rect = group_rect.united(item->mapToScene(item->boundingRect()).boundingRect());
const QPointF p = item->pos();
if (first) {
top_left = p;
first = false;
} else {
if (p.x() < top_left.x()) top_left.setX(p.x());
if (p.y() < top_left.y()) top_left.setY(p.y());
}
}
QSettings settings;
const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"),
Diagram::xGrid).toInt();
const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
Diagram::yGrid).toInt();
const QPointF grid_origin(
qRound(group_rect.topLeft().x() / xGrid) * xGrid,
qRound(group_rect.topLeft().y() / yGrid) * yGrid);
qRound(top_left.x() / xGrid) * xGrid,
qRound(top_left.y() / yGrid) * yGrid);
//Store each item's position. moveTo() applies a grid-snapped
//delta from the baseline, so items preserve their layout and