一键导入
一键导入
| name | qtpass-fixing |
| description | Bug fixing workflow for QtPass - find, fix, test, PR |
| license | GPL-3.0-or-later |
| metadata | {"audience":"developers","workflow":"bugfix"} |
gh issue list --search "<keywords>"grep -r "pattern" src/qtpass-testing skill)tests/auto/<module>/# Build and run tests
make check
# Build binary
make -j4
# Create branch
git checkout -b fix/<issue-number>-short-description
# Commit with issue reference (always use -S for signing)
git commit -S -m "Fix <description> (#issue)"
# Push and create PR
git push origin fix/<branch>
gh pr create --title "Fix <description>" --body "## Summary\n- Fix description"
When CodeRabbit/CodeAnt-AI flag issues in PRs:
# After fixing, amend commit and force push
git add -A
git commit -S --amend --no-edit
git push --force
Qt classes like QGuiApplication::screenAt() can return null on some platforms.
Always verify pointers before dereferencing:
QScreen *screen = QGuiApplication::screenAt(pos);
if (!screen)
return;
Use QCoreApplication::arguments() instead of raw argv[] to let Qt normalize paths and handle quoting:
QStringList args = QCoreApplication::arguments();
Before using optional tools like pass-otp, check availability:
if (!passOTPexists) {
showError("pass-otp is required for OTP features");
return;
}
Use QDir for path normalization to handle / vs \ and ./.. components:
QDir dir;
QString normalized = dir.cleanPath(gpgIdPath);
For regular expression patterns matching URLs or special characters, use \\S+ for non-whitespace to avoid matching spaces:
QRegularExpression urlRegex("(\\S+)://(\\S+)");
For monospace fonts in tables/lists, use setStyleHint(QFont::Monospace) to avoid platform-specific defaults.
Avoid assertions that always evaluate to true:
// Bad - always true
QVERIFY(profiles.isEmpty() || !profiles.isEmpty());
QVERIFY(!store.isEmpty() || store.isEmpty());
// Good - meaningful check
QVERIFY(profiles.isEmpty());
QVERIFY2(store.isEmpty() || store.startsWith("/"), "Pass store should be empty or a plausible path");
Always verify that test setup operations succeed:
// Bad - ignores return value
(void)QDir(srcDir.path()).mkdir("source");
(void)f1.open(QFile::WriteOnly);
// Good - verify success
QVERIFY(QDir(srcDir.path()).mkdir("source"));
QVERIFY(f1.open(QFile::WriteOnly));
Use "cannot" instead of "can't" for formal consistency:
// Bad
// On Windows, we can't safely backup
// Good
// On Windows, we cannot safely backup
Use the actual year the file was created — never a placeholder, never a year range. Repository convention is a single literal year (the existing files are 2014/2015/2016/2018/2020/2026, all real years; PR #1162 review explicitly flagged YYYY placeholders).
// Bad — placeholder; reviewers will ask for a real year
// SPDX-FileCopyrightText: YYYY Your Name
// Bad — year range
// SPDX-FileCopyrightText: 2014-2026 Your Name
// Good — single real year
// SPDX-FileCopyrightText: 2026 Your Name
When showing an error dialog followed by reject() in a constructor, add return to prevent the constructor from continuing with invalid state:
// After error dialog in constructor
if (users.isEmpty()) {
QMessageBox::critical(parent, tr("Error"), tr("No users found"));
reject();
return; // Prevent constructor from continuing
}
When storing references to list items in UI (e.g., Qt::UserRole), prefer indices over pointers to avoid dangling references:
// Instead of storing pointer to local reference
for (const auto &user : m_userList) {
item->setData(Qt::UserRole, QVariant::fromValue(&user)); // DANGLING!
}
// Store index instead
for (int i = 0; i < m_userList.size(); ++i) {
item->setData(Qt::UserRole, QVariant::fromValue(i));
}
// Later, lookup by index
bool success = false;
const int index = item->data(Qt::UserRole).toInt(&success);
if (success && index >= 0 && index < m_userList.size()) {
m_userList[index].enabled = item->checkState() == Qt::Checked;
}
For list items or labels indicating status (e.g., secret keys), prefer QPalette colors over hardcoded values for better accessibility:
// Hardcoded may have poor contrast on some themes
item->setForeground(Qt::blue);
// Theme-aware alternative
const QPalette palette = QApplication::palette();
item->setForeground(palette.color(QPalette::Link));
When indicating status through colors (invalid, expired, partial), add text prefixes or tooltips for color blind users:
// Color only - not accessible
item->setBackground(Qt::darkRed);
// Color + text + tooltip - accessible
item->setBackground(Qt::darkRed);
item->setText(tr("[INVALID] ") + originalText);
item->setToolTip(tr("Invalid key"));
Add debug logging for validation failures using #ifdef QT_DEBUG:
bool success = false;
const int index = item->data(Qt::UserRole).toInt(&success);
if (!success) {
#ifdef QT_DEBUG
qWarning() << "UsersDialog::itemChange: invalid user index data for item";
#endif
return;
}
if (index < 0 || index >= m_userList.size()) {
#ifdef QT_DEBUG
qWarning() << "UsersDialog::itemChange: user index out of range:" << index;
#endif
return;
}
CI tests both Qt 5.15 and Qt 6.8. Some Qt 6 APIs do not exist in Qt 5.15:
| Qt 6 API | Qt 5.15-compatible alternative |
|---|---|
QStringList::removeIf() | erase/remove_if with a predicate lambda |
QList::removeIf() | same pattern |
// Bad — Qt 6.1+ only, fails on Qt 5.15
env.removeIf([&key](const QString &e) { return e.startsWith(key); });
// Good — works on Qt 5.15 and Qt 6.x
env.erase(std::remove_if(env.begin(), env.end(),
[&key](const QString &entry) {
return entry.startsWith(key);
}),
env.end());
When the goal is to drop env vars by name prefix, use std::remove_if with startsWith. Don't reach for QStringList::filter() — it does the opposite (keeps matching entries) and matches substrings anywhere in the string, so even as a selection it would over-match:
// Bad — filter() returns entries containing key, also matching "FOOBAR=value"
// when key is "FOO". And it selects, not removes; the assignment then drops
// every entry that didn't match — opposite of the intent.
env = env.filter(key);
// Good — explicit prefix-based removal
env.erase(std::remove_if(env.begin(), env.end(),
[&key](const QString &e) { return e.startsWith(key); }),
env.end());
When iterating over a Qt container that is a non-const member variable, add std::as_const() to prevent Qt from making an implicit copy (detach):
// Bad — may detach the container implicitly
for (const auto &field : m_fields) { ... }
// Good — no detach, no copy
for (const auto &field : std::as_const(m_fields)) { ... }
When comparing booleans, use && instead of == to avoid unexpected true values:
// Bad - yields true when both are false (e.g., public key case)
bool secret = false;
bool isSec = (type == GpgRecordType::Sec); // false
handlePubSecRecord(props, secret == isSec); // false == false = true!
// Good - requires both conditions to be true
handlePubSecRecord(props, secret && (type == GpgRecordType::Sec));
| File | Purpose |
|---|---|
| src/mainwindow.cpp | Main UI, tree view, dialogs |
| src/pass.cpp | GPG operations, path handling |
| src/util.cpp | Utilities, regular expression, file ops |
| src/filecontent.cpp | Password file parsing |
| src/imitatepass.cpp | CLI pass imitation |
| src/configdialog.cpp | Settings dialog |
| src/executor.cpp | Command execution |
See qtpass-linting skill for full CI workflow. Pattern:
# Run linter locally BEFORE pushing
act push -W .github/workflows/linter.yml -j build
npx prettier --write <config-file>
npx prettier --write .github/workflows/*.yml
npx prettier --write "*/*/SKILL.md"
# Check formatting
clang-format --style=file --dry-run <source-file>
# Apply formatting
clang-format --style=file -i <source-file>
UI bugs often involve signal/slot connections, widget state, or clipboard operations.
Common patterns:
Debugging:
// Add debug output
qDebug() << "Button clicked, state:" << ui->someWidget->isVisible();
GPG-related bugs often involve command execution, path handling, or key operations.
Common patterns:
Debugging:
// Enable verbose GPG output
// Check /tmp for temporary GPG files
Path handling bugs often involve separators, encoding, or model indexing.
Common patterns:
Debugging:
qDebug() << "Path:" << path << "Cleaned:" << QDir::cleanPath(path);
For proper code analysis (resolving Qt types like QString), generate compile_commands.json:
./scripts/generate-compile-commands.sh
This enables clangd/LSP to provide accurate completions and catch real issues. Without it, the LSP shows false positives about missing Qt headers.
When the LSP shows "(fix available)" on errors:
Ctrl+. (Quick Fix)Alt+Enter on the errortextDocument/codeAction via your LSP pluginNote: LSP suggestions are just that - suggestions. Always verify the fix makes sense before applying, especially for complex refactoring.
To check a file directly for issues:
# Generate compile_commands.json first (required for Qt headers)
./scripts/generate-compile-commands.sh
# Check a specific file
clangd --check=/path/to/file.cpp
Common clangd diagnostics:
[performance-unnecessary-copy-initialization] - Use const T& instead of const T[readability-static-definition] - Consider making static definitions inlineQtPass CI/CD workflow - run GitHub Actions locally with act, linters, formatters
QtPass localization workflow - translation files, updating, adding languages
QtPass localization audit - structural checks on .ts files (placeholders, HTML balance, mnemonics, mixed-script artifacts)
QtPass GitHub interaction - PRs, issues, branches, merging
Documentation guide for QtPass - README, FAQ, localization
Release workflow for QtPass - versioning, builds, publishing