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

+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 "$@"