Adds qt migration example

This commit is contained in:
torsten committed 2026-08-05 15:29:14 +02:00
1 parent e8488dd40e
commit 044885dddb
19 files changed
+1294 -2

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
FROM debian:bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends \
python3 \
clang-format \
cmake \
ninja-build \
git \
g++ \
make \
ca-certificates \
pkg-config \
qtbase5-dev \
qt6-base-dev \
qt6-base-dev-tools \
qt6-tools-dev \
qt6-tools-dev-tools && \
rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
Vendored
+84
View File
@@ -0,0 +1,84 @@
pipeline {
agent {
docker {
image 'lifemaps/ci-cpp:latest'
reuseNode true
args '-u root:root'
}
}
options {
timestamps()
disableConcurrentBuilds()
}
environment {
PROJECT_DIR = "qt6-after/qt6-migrated-benchmark"
BUILD_DIR = "build"
CHANGE_TARGET = "${env.CHANGE_TARGET ?: 'origin/main'}"
}
stages {
stage('Checkout') {
steps {
checkout scm
sh 'git fetch --all --prune'
sh 'git submodule update --init --recursive || true'
}
}
stage('Tool Versions') {
steps {
sh '''
python3 --version
cmake --version
ninja --version
g++ --version
'''
}
}
stage('Qt Diagnostics') {
steps {
sh '''
dpkg -l | grep -E "qt6-base-dev|qtbase5-dev|qt6-tools" || true
pkg-config --list-all | grep -i qt || true
'''
}
}
stage('Policy Checks') {
steps {
sh 'python3 ci/check_pr_policies.py'
}
}
stage('Configure') {
steps {
sh 'cmake -S ${PROJECT_DIR} -B ${BUILD_DIR} -G Ninja -DCMAKE_BUILD_TYPE=Debug'
}
}
stage('Build') {
steps {
sh 'cmake --build ${BUILD_DIR} --parallel'
}
}
stage('Test') {
steps {
sh 'ctest --test-dir ${BUILD_DIR} --output-on-failure'
}
}
}
post {
always {
script {
if (getContext(hudson.FilePath)) {
cleanWs deleteDirs: true, disableDeferredWipeout: true
}
}
}
}
}
+68 -2
View File
@@ -1,3 +1,69 @@
# qt-migration-copilot-demo
# Qt Migration Copilot Demo
Qt Migration Copilot Demo
Dieses Repository zeigt einen kleinen, reproduzierbaren Benchmark für die Migration eines alten Qt5/C++11-Projekts nach Qt6/C++20 mit Copilot CLI.
## Inhalt
- `qt5-before/`
Ausgangszustand mit absichtlich veralteten Qt5-APIs:
- `QRegExp`
- `QDesktopWidget`
- `QTextCodec`
- alte `SIGNAL/SLOT`-Syntax
- getrennte `Qt5::Core`, `Qt5::Gui`, `Qt5::Widgets`
- `qt6-after/`
Erwarteter oder referenzierter Zielzustand nach der Migration:
- `QRegularExpression`
- `QScreen`
- modernes UTF-8-/String-Handling
- moderne Connect-Syntax
- Qt6 + C++20
- `Dockerfile.ci`
Vorgebautes CI-Image für Jenkins.
- `Jenkinsfile`
Führt Configure, Build und Test im vorgebauten Docker-Image aus.
- `scripts/`
Hilfsskripte für Entire CLI und Copilot CLI.
## Ziel
Das Repo dient als:
- Demo für Qt5→Qt6-Migration
- Benchmark für Copilot CLI / Agenten
- reproduzierbares Beispiel mit Entire-Checkpoints
- Referenz für Jenkins + Docker CI
## Empfohlener Workflow
1. In `qt5-before/` starten
2. Entire für Copilot CLI aktivieren
3. Baseline-Checkpoint committen
4. Copilot-Migration ausführen
5. Ergebnis committen
6. Mit `qt6-after/` vergleichen
## CI-Image bauen
```bash
docker build -t lifemaps/ci-cpp:latest -f Dockerfile.ci .
docker push lifemaps/ci-cpp:latest
```
## Entire + Copilot
Beispiel:
```bash
./scripts/run-entire-copilot-migration-with-optional-rewind-resume.sh qt5-before/qt5-legacy-migration-benchmark
```
## Hinweis
Dieses Repository ist absichtlich klein gehalten, damit:
- Migrationsänderungen gut sichtbar bleiben
- Copilot-Antworten vergleichbar sind
- CI-Läufe schnell reproduzierbar sind
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(".").resolve()
TEST_DIRS = [
ROOT / "tests",
ROOT / "test",
ROOT / "qt6-after" / "qt6-migrated-benchmark" / "tests",
]
CPP_EXTENSIONS = {".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"}
TEST_PATTERNS = [
"test_{name}.cpp",
"test_{name}.h",
"{name}_test.cpp",
"{name}_test.h",
"{name}test.cpp",
"{name}test.h",
"{name}gtest.cpp",
"{name}gtest.h",
]
def run(cmd: str) -> str:
return subprocess.check_output(cmd, shell=True, text=True).strip()
def git_ref_exists(ref: str) -> bool:
try:
subprocess.check_call(
f"git rev-parse --verify --quiet '{ref}'",
shell=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
return True
except subprocess.CalledProcessError:
return False
def is_pull_request_build() -> bool:
return bool(os.environ.get("CHANGE_ID"))
def resolve_target_ref() -> str | None:
raw = os.environ.get("CHANGE_TARGET", "").strip()
candidates = []
if raw:
candidates.append(raw)
if not raw.startswith("origin/"):
candidates.append(f"origin/{raw}")
candidates.extend(["origin/main", "main"])
seen = set()
for ref in candidates:
if ref and ref not in seen:
seen.add(ref)
if git_ref_exists(ref):
return ref
return None
def changed_files():
target = resolve_target_ref()
if target:
print(f"Vergleiche gegen Ziel-Ref: {target}")
out = run(f"git diff --name-only {target}...HEAD")
return [line.strip() for line in out.splitlines() if line.strip()]
if is_pull_request_build():
print("ERROR: Kein gültiger Ziel-Branch-Ref für PR-Build gefunden.")
sys.exit(1)
if git_ref_exists("HEAD~1"):
print("Kein Ziel-Branch-Ref gefunden, verwende HEAD~1 für Branch-Build.")
out = run("git diff --name-only HEAD~1..HEAD")
return [line.strip() for line in out.splitlines() if line.strip()]
print("Keine Vergleichsbasis gefunden, behandle alle versionierten Dateien als geändert.")
out = run("git ls-files")
return [line.strip() for line in out.splitlines() if line.strip()]
def is_cpp_file(path: Path) -> bool:
return path.suffix.lower() in CPP_EXTENSIONS
def require_clang_format():
if not (ROOT / ".clang-format").exists():
print("ERROR: .clang-format fehlt im Repository-Root.")
sys.exit(1)
def check_format(path: Path):
subprocess.check_call(f"clang-format --dry-run --Werror '{path}'", shell=True)
def candidate_names(path: Path):
stem = path.stem.lower()
names = {stem}
for suffix in ["_p", "_impl", "_private", "_priv", "_ui"]:
if stem.endswith(suffix):
names.add(stem[:-len(suffix)])
return sorted(n for n in names if n)
def iter_test_files():
for test_dir in TEST_DIRS:
if test_dir.exists():
yield from (p for p in test_dir.rglob("*") if p.is_file())
def has_related_test(path: Path) -> bool:
test_files = list(iter_test_files())
if not test_files:
return False
test_names = [p.name.lower() for p in test_files]
for name in candidate_names(path):
for pattern in TEST_PATTERNS:
if pattern.format(name=name).lower() in test_names:
return True
for test_name in test_names:
if name in test_name and ("test" in test_name or "gtest" in test_name):
return True
return False
def main():
require_clang_format()
files = changed_files()
if not files:
print("Keine geänderten Dateien erkannt.")
return
for rel in files:
path = ROOT / rel
if not path.exists():
continue
if is_cpp_file(path):
print(f"Prüfe: {rel}")
try:
check_format(path)
except subprocess.CalledProcessError:
print(f"ERROR: Formatfehler in {rel}")
sys.exit(1)
if not has_related_test(path):
print(f"ERROR: Kein passender Test unter tests/, test/ oder qt6-after/qt6-migrated-benchmark/tests für {rel} gefunden.")
sys.exit(1)
print("Policy-Checks erfolgreich.")
if __name__ == "__main__":
main()
Whitespace-only changes.
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.10)
project(Qt5LegacyMigrationBenchmark LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets)
add_executable(Qt5LegacyMigrationBenchmark
main.cpp
MainWindow.h
MainWindow.cpp
MainWindow.ui
resources.qrc
)
target_link_libraries(Qt5LegacyMigrationBenchmark
Qt5::Core
Qt5::Gui
Qt5::Widgets
)
@@ -0,0 +1,53 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QTextCodec>
#include <QPixmap>
#include <QString>
#include <QRect>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, legacyPattern("^[A-Za-z]+\\d+$")
{
ui->setupUi(this);
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QString title = codec->toUnicode("Qt5 Legacy Migration Benchmark");
setWindowTitle(title);
ui->label->setText(codec->toUnicode("Bitte Text wie Test123 eingeben."));
ui->iconLabel->setPixmap(QPixmap(":/icons/app.png").scaled(48, 48));
connect(ui->validateButton, SIGNAL(clicked()), this, SLOT(onValidateClicked()));
connect(ui->screenButton, SIGNAL(clicked()), this, SLOT(onScreenInfoClicked()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onValidateClicked()
{
QString text = ui->lineEdit->text();
if (legacyPattern.exactMatch(text)) {
ui->resultLabel->setText("Match");
} else {
ui->resultLabel->setText("No match");
}
}
void MainWindow::onScreenInfoClicked()
{
QRect screenGeometry = QApplication::desktop()->screenGeometry();
QString info = QString("Screen: %1x%2")
.arg(screenGeometry.width())
.arg(screenGeometry.height());
ui->resultLabel->setText(info);
}
@@ -0,0 +1,27 @@
#pragma once
#include <QMainWindow>
#include <QRegExp>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void onValidateClicked();
void onScreenInfoClicked();
private:
Ui::MainWindow *ui;
QRegExp legacyPattern;
};
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>420</width>
<height>240</height>
</rect>
</property>
<property name="windowTitle">
<string>Qt5 Legacy App</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Initial text</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Enter value like Test123</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="iconLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QPushButton" name="validateButton">
<property name="text">
<string>Validate</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="screenButton">
<property name="text">
<string>Screen Info</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="resultLabel">
<property name="text">
<string>Result appears here</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

@@ -0,0 +1,16 @@
#include <QApplication>
#include <QTextCodec>
#include "MainWindow.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#endif
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/icons">
<file>app.png</file>
</qresource>
</RCC>
Whitespace-only changes.
Whitespace-only changes.
+216
View File
@@ -0,0 +1,216 @@
# scripts/
Dieses Verzeichnis enthält Hilfsskripte für den Demo- und Benchmark-Workflow rund um:
- **Qt5 → Qt6**
- **C++11 → C++23**
- **Copilot CLI**
- **Entire CLI**
- **Scaleway BYOK / OpenAI-kompatibler Provider**
- **lokale Reproduzierbarkeit der Migration**
## Übersicht
### `create-qt5-legacy-benchmark.sh`
Erzeugt das Legacy-Beispielprojekt unter:
```text
qt5-before/qt5-legacy-migration-benchmark
```
Das Projekt ist absichtlich veraltet aufgebaut, um eine realistische Migration zu erzwingen, z. B. mit:
- `QRegExp`
- `QDesktopWidget`
- `QTextCodec`
- alter `SIGNAL/SLOT`-Syntax
- `Qt5::Core`, `Qt5::Gui`, `Qt5::Widgets`
- `C++11`
### `run-entire-copilot-migration.sh`
Standard-Workflow für eine Copilot-CLI-Migration mit Entire:
1. Entire für Copilot CLI aktivieren
2. Baseline committen
3. Copilot-Session starten
4. Ergebnis committen
5. Checkpoints anzeigen
6. letzten Stand mit Entire erklären
Dieses Skript ist für den normalen Migrationslauf gedacht.
### `run-entire-copilot-migration-with-optional-rewind-resume.sh`
Wie das Standardskript, aber zusätzlich mit optionalem:
- `entire checkpoint rewind`
- `entire session resume <branch>`
**Wichtig:**
Die Entire-Dokumentation ist bei Copilot CLI in Bezug auf Rewind/Resume nicht ganz konsistent.
Die Copilot-CLI-Seite beschreibt Rewind und Resume, während die allgemeine Agent-Übersicht teils eingeschränkte Aussagen macht. Entire installiert Copilot-CLI-Hooks mit `entire agent add copilot-cli`, erstellt Checkpoints bei Commits und speichert die Daten auf `entire/checkpoints/v1`. ([docs.entire.io](https://docs.entire.io/agents/copilot-cli?utm_source=openai))
Darum behandelt dieses Skript Rewind/Resume bewusst als **optional** und **fehlertolerant**. Diese Vorsicht ist eine Schlussfolgerung aus den widersprüchlichen Entire-Seiten. ([docs.entire.io](https://docs.entire.io/agents/copilot-cli?utm_source=openai))
### `use-scaleway-copilot-debug.sh`
Hilfsskript zum Setzen einer **Scaleway OpenAI-kompatiblen BYOK-Konfiguration** für Copilot CLI.
Typischer Zweck:
- Provider-Umgebungsvariablen setzen
- Copilot CLI mit Scaleway testen
- Modellname konfigurieren
- Token-Limits für nicht im Copilot-Katalog enthaltene Modelle explizit setzen
- Debugging eines externen Providers vereinfachen
Ein typischer Inhalt wäre z. B.:
- `COPILOT_PROVIDER_TYPE=openai`
- `COPILOT_PROVIDER_BASE_URL=https://api.scaleway.ai/v1`
- `COPILOT_PROVIDER_API_KEY=...`
- `COPILOT_MODEL=qwen3.5-397b-a17b`
- `COPILOT_PROVIDER_MAX_PROMPT_TOKENS=...`
- `COPILOT_PROVIDER_MAX_OUTPUT_TOKENS=...`
GitHub Copilot CLI BYOK verwendet für OpenAI-kompatible Provider genau dieses Muster aus Base-URL, API-Key und Modell. ([docs.entire.io](https://docs.entire.io/agents/copilot-cli?utm_source=openai))
---
## Empfohlene Reihenfolge
### 1. Legacy-Projekt erzeugen
```bash
./scripts/create-qt5-legacy-benchmark.sh
```
### 2. Optional: Copilot mit Scaleway konfigurieren
```bash
source ./scripts/use-scaleway-copilot-debug.sh
```
Danach kannst du prüfen, ob die Variablen gesetzt sind:
```bash
echo "$COPILOT_PROVIDER_BASE_URL"
echo "$COPILOT_MODEL"
```
### 3. Migration mit Entire + Copilot starten
Standard:
```bash
./scripts/run-entire-copilot-migration.sh qt5-before/qt5-legacy-migration-benchmark
```
Mit optionalem Rewind/Resume:
```bash
./scripts/run-entire-copilot-migration-with-optional-rewind-resume.sh qt5-before/qt5-legacy-migration-benchmark
```
---
## Copilot-Prompt für die Migration
Die Migrationsskripte verwenden sinngemäß einen Prompt in dieser Richtung:
```text
Migrate the legacy Qt5/C++11 desktop application to Qt6/C++23.
Requirements:
- Read the legacy source project from qt5-before/qt5-legacy-migration-benchmark
- Write the migrated project into qt6-after/qt6-migrated-benchmark
- Do not modify the legacy source tree in place
- Update CMake from Qt5 to Qt6
- Keep Core, Gui, and Widgets explicitly linked
- Replace QRegExp with QRegularExpression
- Replace QDesktopWidget / QApplication::desktop() usage with QScreen-based Qt6 APIs
- Remove legacy QTextCodec usage and use modern Qt6-safe text handling
- Modernize old SIGNAL/SLOT syntax to function-pointer connect syntax
- Replace legacy null pointer literals with nullptr
- Preserve the .ui and .qrc integration
- Preserve application behavior
- Upgrade the project from C++11 to C++23
- For every code change inside a method, add at least three tests covering the changed behavior
- Place all generated tests in qt6-after/qt6-migrated-benchmark/tests
- Update CMake so the migrated application and all tests build successfully with CTest
- Keep the test file naming compatible with repository policy checks
- Briefly explain each migration change
```
---
## Entire-Hinweise
Entire erfasst bei unterstützten Agenten u. a.:
- Gesprächsverlauf
- Dateiveränderungen
- Zeitstempel
- Checkpoints bei Commits
- Session-Kontext auf dem Branch `entire/checkpoints/v1` ([docs.entire.io](https://docs.entire.io/agents/copilot-cli?utm_source=openai))
Typischer Entire-Grundablauf:
```bash
entire agent add copilot-cli
copilot
git add .
git commit -m "..."
entire checkpoint list
```
Dieser Ablauf ist direkt in der Copilot-CLI- und Quickstart-Dokumentation beschrieben. ([docs.entire.io](https://docs.entire.io/agents/copilot-cli?utm_source=openai))
---
## Hinweise zu `use-scaleway-copilot-debug.sh`
Das Skript sollte **gesourct** werden, nicht als eigener Prozess laufen, damit exportierte Variablen in deiner aktuellen Shell erhalten bleiben:
```bash
source ./scripts/use-scaleway-copilot-debug.sh
```
oder kurz:
```bash
. ./scripts/use-scaleway-copilot-debug.sh
```
Außerdem:
- keine echten Secrets committen
- API-Keys nicht im Repo speichern
- sensible Werte besser aus bereits gesetzten Umgebungsvariablen übernehmen
Beispiel:
```bash
export SCW_SECRET_KEY="..."
source ./scripts/use-scaleway-copilot-debug.sh
```
---
## Empfohlene Erweiterungen
Optional kannst du später noch ergänzen:
- `build-qt6-after.sh`
- `run-ctest-qt6-after.sh`
- `compare-before-after.sh`
- `capture-copilot-env.sh`
---
## Kurzfassung
Der `scripts/`-Ordner enthält alles, um:
1. ein Legacy-Qt5-Projekt zu erzeugen,
2. Copilot CLI optional mit Scaleway zu konfigurieren,
3. eine Qt6/C++23-Migration mit Entire zu dokumentieren,
4. und den Migrationslauf reproduzierbar zu machen.
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env bash
set -euo pipefail
TARGET_DIR="${1:-qt5-before/qt5-legacy-migration-benchmark}"
mkdir -p "${TARGET_DIR}"
cat > "${TARGET_DIR}/CMakeLists.txt" <<'FILE_EOF'
cmake_minimum_required(VERSION 3.10)
project(Qt5LegacyMigrationBenchmark LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets)
add_executable(Qt5LegacyMigrationBenchmark
main.cpp
MainWindow.h
MainWindow.cpp
MainWindow.ui
resources.qrc
)
target_link_libraries(Qt5LegacyMigrationBenchmark
Qt5::Core
Qt5::Gui
Qt5::Widgets
)
FILE_EOF
cat > "${TARGET_DIR}/MainWindow.h" <<'FILE_EOF'
#pragma once
#include <QMainWindow>
#include <QRegExp>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void onValidateClicked();
void onScreenInfoClicked();
private:
Ui::MainWindow *ui;
QRegExp legacyPattern;
};
FILE_EOF
cat > "${TARGET_DIR}/MainWindow.cpp" <<'FILE_EOF'
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QTextCodec>
#include <QPixmap>
#include <QString>
#include <QRect>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, legacyPattern("^[A-Za-z]+\\d+$")
{
ui->setupUi(this);
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QString title = codec->toUnicode("Qt5 Legacy Migration Benchmark");
setWindowTitle(title);
ui->label->setText(codec->toUnicode("Bitte Text wie Test123 eingeben."));
ui->iconLabel->setPixmap(QPixmap(":/icons/app.png").scaled(48, 48));
connect(ui->validateButton, SIGNAL(clicked()), this, SLOT(onValidateClicked()));
connect(ui->screenButton, SIGNAL(clicked()), this, SLOT(onScreenInfoClicked()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onValidateClicked()
{
QString text = ui->lineEdit->text();
if (legacyPattern.exactMatch(text)) {
ui->resultLabel->setText("Match");
} else {
ui->resultLabel->setText("No match");
}
}
void MainWindow::onScreenInfoClicked()
{
QRect screenGeometry = QApplication::desktop()->screenGeometry();
QString info = QString("Screen: %1x%2")
.arg(screenGeometry.width())
.arg(screenGeometry.height());
ui->resultLabel->setText(info);
}
FILE_EOF
cat > "${TARGET_DIR}/main.cpp" <<'FILE_EOF'
#include <QApplication>
#include <QTextCodec>
#include "MainWindow.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#endif
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
FILE_EOF
cat > "${TARGET_DIR}/MainWindow.ui" <<'FILE_EOF'
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>420</width>
<height>240</height>
</rect>
</property>
<property name="windowTitle">
<string>Qt5 Legacy App</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Initial text</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Enter value like Test123</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="iconLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QPushButton" name="validateButton">
<property name="text">
<string>Validate</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="screenButton">
<property name="text">
<string>Screen Info</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="resultLabel">
<property name="text">
<string>Result appears here</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
FILE_EOF
cat > "${TARGET_DIR}/resources.qrc" <<'FILE_EOF'
<RCC>
<qresource prefix="/icons">
<file>app.png</file>
</qresource>
</RCC>
FILE_EOF
(
cd "${TARGET_DIR}"
python3 - <<'FILE_EOF'
import base64
png = base64.b64decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wn8nKsAAAAASUVORK5CYII="
)
with open("app.png", "wb") as f:
f.write(png)
FILE_EOF
)
echo "Qt5 legacy benchmark created at: ${TARGET_DIR}"
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR="${1:-qt5-before/qt5-legacy-migration-benchmark}"
need_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "ERROR: '$1' not found in PATH." >&2
exit 1
}
}
need_cmd entire
need_cmd copilot
need_cmd git
if [ ! -d "$PROJECT_DIR" ]; then
echo "ERROR: project directory '$PROJECT_DIR' not found." >&2
exit 1
fi
cd "$PROJECT_DIR"
if [ ! -d .git ]; then
git init
fi
entire agent add copilot-cli
entire status
git add .
if git diff --cached --quiet; then
git commit --allow-empty -m "baseline: Qt5/C++11 legacy benchmark before Qt6/C++23 migration"
else
git commit -m "baseline: Qt5/C++11 legacy benchmark before Qt6/C++23 migration"
fi
entire checkpoint list
cat <<'PROMPT'
Use this prompt in Copilot CLI:
Migrate the legacy Qt5/C++11 desktop application to Qt6/C++23.
Requirements:
- Read the legacy source project from qt5-before/qt5-legacy-migration-benchmark
- Write the migrated project into qt6-after/qt6-migrated-benchmark
- Do not modify the legacy source tree in place
- Update CMake from Qt5 to Qt6
- Keep Core, Gui, and Widgets explicitly linked
- Replace QRegExp with QRegularExpression
- Replace QDesktopWidget / QApplication::desktop() usage with QScreen-based Qt6 APIs
- Remove legacy QTextCodec usage and use modern Qt6-safe text handling
- Modernize old SIGNAL/SLOT syntax to function-pointer connect syntax
- Replace legacy null pointer literals with nullptr
- Preserve the .ui and .qrc integration
- Preserve application behavior
- Upgrade the project from C++11 to C++23
- For every code change inside a method, add at least three tests covering the changed behavior
- Place all generated tests in qt6-after/qt6-migrated-benchmark/tests
- Update CMake so the migrated application and all tests build successfully with CTest
- Keep the test file naming compatible with repository policy checks
- Briefly explain each migration change
PROMPT
copilot
git status --short
git add .
if git diff --cached --quiet; then
echo "No changes to commit."
else
git commit -m "migrate: Qt5/C++11 app to Qt6/C++23 with tests and modern Qt APIs"
fi
entire checkpoint list
entire checkpoint explain HEAD || true
read -r -p "Optional Rewind testen? [y/N] " DO_REWIND
if [[ "${DO_REWIND:-N}" =~ ^[Yy]$ ]]; then
if ! entire checkpoint rewind; then
echo "WARN: rewind not supported or failed."
fi
fi
read -r -p "Optional Session-Resume-Hinweis ausgeben? [y/N] " DO_RESUME
if [[ "${DO_RESUME:-N}" =~ ^[Yy]$ ]]; then
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
if ! entire session resume "${CURRENT_BRANCH}"; then
echo "WARN: resume not supported or failed."
fi
fi
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR="${1:-qt5-before/qt5-legacy-migration-benchmark}"
need_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "ERROR: '$1' not found in PATH." >&2
exit 1
}
}
need_cmd entire
need_cmd copilot
need_cmd git
if [ ! -d "$PROJECT_DIR" ]; then
echo "ERROR: project directory '$PROJECT_DIR' not found." >&2
exit 1
fi
cd "$PROJECT_DIR"
if [ ! -d .git ]; then
git init
fi
entire agent add copilot-cli
entire status
git add .
if git diff --cached --quiet; then
git commit --allow-empty -m "baseline: Qt5/C++11 legacy benchmark before Qt6/C++23 migration"
else
git commit -m "baseline: Qt5/C++11 legacy benchmark before Qt6/C++23 migration"
fi
entire checkpoint list
cat <<'PROMPT'
Use this prompt in Copilot CLI:
Migrate the legacy Qt5/C++11 desktop application to Qt6/C++23.
Requirements:
- Read the legacy source project from qt5-before/qt5-legacy-migration-benchmark
- Write the migrated project into qt6-after/qt6-migrated-benchmark
- Do not modify the legacy source tree in place
- Update CMake from Qt5 to Qt6
- Keep Core, Gui, and Widgets explicitly linked
- Replace QRegExp with QRegularExpression
- Replace QDesktopWidget / QApplication::desktop() usage with QScreen-based Qt6 APIs
- Remove legacy QTextCodec usage and use modern Qt6-safe text handling
- Modernize old SIGNAL/SLOT syntax to function-pointer connect syntax
- Replace legacy null pointer literals with nullptr
- Preserve the .ui and .qrc integration
- Preserve application behavior
- Upgrade the project from C++11 to C++23
- For every code change inside a method, add at least three tests covering the changed behavior
- Place all generated tests in qt6-after/qt6-migrated-benchmark/tests
- Update CMake so the migrated application and all tests build successfully with CTest
- Keep the test file naming compatible with repository policy checks
- Briefly explain each migration change
PROMPT
copilot
git status --short
git add .
if git diff --cached --quiet; then
echo "No changes to commit."
exit 0
fi
git commit -m "migrate: Qt5/C++11 app to Qt6/C++23 with tests and modern Qt APIs"
entire checkpoint list
entire checkpoint explain HEAD || true
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -euo pipefail
red() { printf "\033[31m%s\033[0m\n" "$*"; }
green() { printf "\033[32m%s\033[0m\n" "$*"; }
yellow() { printf "\033[33m%s\033[0m\n" "$*"; }
blue() { printf "\033[34m%s\033[0m\n" "$*"; }
fail() {
red "FEHLER: $*"
exit 1
}
check_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "'$1' nicht gefunden"
}
mask() {
local s="${1:-}"
local n=${#s}
if [ "$n" -le 8 ]; then
printf '********'
else
printf '%s****%s' "${s:0:4}" "${s: -4}"
fi
}
check_env() {
[ -n "${COPILOT_PROVIDER_TYPE:-}" ] || fail "COPILOT_PROVIDER_TYPE fehlt"
[ -n "${COPILOT_PROVIDER_API_KEY:-}" ] || fail "COPILOT_PROVIDER_API_KEY fehlt"
[ -n "${COPILOT_PROVIDER_BASE_URL:-}" ] || fail "COPILOT_PROVIDER_BASE_URL fehlt"
[ -n "${COPILOT_MODEL:-}" ] || fail "COPILOT_MODEL fehlt"
}
print_env_summary() {
blue "== Konfiguration =="
echo "Provider : ${COPILOT_PROVIDER_TYPE}"
echo "Base URL : ${COPILOT_PROVIDER_BASE_URL}"
echo "Modell : ${COPILOT_MODEL}"
echo "API Key : $(mask "${COPILOT_PROVIDER_API_KEY}")"
echo
}
probe_models() {
blue "== Prüfe Models-Endpoint =="
local models_url="${COPILOT_PROVIDER_BASE_URL%/}/models"
local http_code
http_code="$(curl -sS -o /tmp/copilot_models_probe.json \
-w "%{http_code}" \
-H "Authorization: Bearer ${COPILOT_PROVIDER_API_KEY}" \
"$models_url" || true)"
echo "GET $models_url"
echo "HTTP $http_code"
if [ "$http_code" = "200" ]; then
green "Models-Endpoint erreichbar"
if command -v jq >/dev/null 2>&1; then
echo
yellow "Gefundene Modelle (erste 20):"
jq -r '
if .data then .data[]?.id
elif type=="array" then .[]?.id
else empty end
' /tmp/copilot_models_probe.json | head -20
echo
if jq -e --arg model "${COPILOT_MODEL}" '
if .data then any(.data[]?; .id == $model)
elif type=="array" then any(.[]?; .id == $model)
else false end
' /tmp/copilot_models_probe.json >/dev/null 2>&1; then
green "Modell '${COPILOT_MODEL}' im Models-Endpoint gefunden"
else
yellow "Modell '${COPILOT_MODEL}' nicht eindeutig im Models-Endpoint gefunden"
fi
else
yellow "jq nicht installiert; JSON-Antwort nicht geparst"
fi
else
red "Models-Endpoint nicht erfolgreich erreichbar"
echo
sed -n '1,120p' /tmp/copilot_models_probe.json || true
echo
fi
}
probe_chat() {
blue "== Prüfe Chat-Completions minimal =="
local chat_url="${COPILOT_PROVIDER_BASE_URL%/}/chat/completions"
cat >/tmp/copilot_chat_probe.json <<JSON
{
"model": "${COPILOT_MODEL}",
"messages": [
{ "role": "user", "content": "Reply with exactly: OK" }
],
"stream": false
}
JSON
local http_code
http_code="$(curl -sS -o /tmp/copilot_chat_probe_response.json \
-w "%{http_code}" \
-H "Authorization: Bearer ${COPILOT_PROVIDER_API_KEY}" \
-H "Content-Type: application/json" \
-d @/tmp/copilot_chat_probe.json \
"$chat_url" || true)"
echo "POST $chat_url"
echo "HTTP $http_code"
if [ "$http_code" = "200" ]; then
green "Chat-Completions funktioniert"
if command -v jq >/dev/null 2>&1; then
echo
yellow "Antwortauszug:"
jq -r '.choices[0].message.content // .choices[0].delta.content // "keine textantwort gefunden"' \
/tmp/copilot_chat_probe_response.json
echo
else
sed -n '1,80p' /tmp/copilot_chat_probe_response.json || true
fi
else
red "Chat-Completions fehlgeschlagen"
echo
sed -n '1,160p' /tmp/copilot_chat_probe_response.json || true
echo
fi
}
run_copilot() {
blue "== Starte Copilot CLI =="
exec copilot "$@"
}
main() {
check_cmd curl
check_cmd copilot
check_env
print_env_summary
probe_models
echo
probe_chat
echo
run_copilot "$@"
}
main "$@"