Adds qt migration example
This commit is contained in:
1 parent
e8488dd40e
commit
044885dddb
19 files changed
+1294
-2
No files matched your search
@@ -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
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Executable
+152
@@ -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.
@@ -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.
|
||||||
Executable
+233
@@ -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
|
||||||
Executable
+79
@@ -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
|
||||||
Executable
+149
@@ -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 "$@"
|
||||||
Reference in new issue
Block a user