commit 5ad2d8c42d1acaba443744584f157c8f4f397e8d
parent 83cda34af336ea2bb2bf4ca2a9a04f6609045769
Author: cfillion <cfillion@users.noreply.github.com>
Date: Wed, 4 May 2016 21:16:42 -0400
implement pinning packages to current version (aka ignore updates)
Diffstat:
9 files changed, 168 insertions(+), 61 deletions(-)
diff --git a/src/browser.cpp b/src/browser.cpp
@@ -43,6 +43,7 @@ enum Action {
ACTION_REINSTALL_ALL,
ACTION_UNINSTALL,
ACTION_UNINSTALL_ALL,
+ ACTION_PIN,
ACTION_CONTENTS,
ACTION_HISTORY,
ACTION_ABOUT,
@@ -138,6 +139,9 @@ void Browser::onCommand(const int id, const int event)
case ACTION_UNINSTALL_ALL:
selectionDo(bind(&Browser::uninstall, this, arg::_1, false));
break;
+ case ACTION_PIN:
+ togglePin(m_currentIndex);
+ break;
case ACTION_CONTENTS:
contents(m_currentIndex);
break;
@@ -148,7 +152,7 @@ void Browser::onCommand(const int id, const int event)
about(m_currentIndex);
break;
case ACTION_RESET_ALL:
- selectionDo(bind(&Browser::resetAction, this, arg::_1));
+ selectionDo(bind(&Browser::resetTarget, this, arg::_1));
break;
case ACTION_REFRESH:
refresh(true);
@@ -201,7 +205,7 @@ void Browser::onContextMenu(HWND target, const int x, const int y)
menu.addAction(AUTO_STR("&Install/update selection"), ACTION_LATEST_ALL);
menu.addAction(AUTO_STR("&Reinstall selection"), ACTION_REINSTALL_ALL);
menu.addAction(AUTO_STR("&Uninstall selection"), ACTION_UNINSTALL_ALL);
- menu.addAction(AUTO_STR("&Clear queued action"), ACTION_RESET_ALL);
+ menu.addAction(AUTO_STR("&Clear queued actions"), ACTION_RESET_ALL);
menu.addSeparator();
}
@@ -275,6 +279,16 @@ void Browser::onContextMenu(HWND target, const int x, const int y)
menu.addSeparator();
+ const UINT pinIndex = menu.addAction(
+ AUTO_STR("&Pin current version"), ACTION_PIN);
+
+ if(!entry->canPin())
+ menu.disable(pinIndex);
+ if(entry->pin.value_or(entry->regEntry.pinned))
+ menu.check(pinIndex);
+
+ menu.addSeparator();
+
menu.setEnabled(!entry->test(ObsoleteFlag),
menu.addAction(AUTO_STR("Package &Contents"), ACTION_CONTENTS));
@@ -487,6 +501,8 @@ void Browser::transferActions()
entryIt->target = target;
}
+ if(oldEntry->pin)
+ entryIt->pin = *oldEntry->pin;
m_actions.insert(&*entryIt);
}
@@ -584,6 +600,8 @@ string Browser::getValue(const Column col, const Entry &entry) const
case StateColumn: {
if(entry.test(ObsoleteFlag))
display += 'o';
+ else if(entry.regEntry.pinned)
+ display += 'p';
else if(entry.test(OutOfDateFlag))
display += 'u';
else if(entry.test(InstalledFlag))
@@ -593,6 +611,8 @@ string Browser::getValue(const Column col, const Entry &entry) const
if(entry.target)
display += *entry.target == nullptr ? 'U' : 'I';
+ if(entry.pin && entry.canPin())
+ display += 'P';
return display;
}
@@ -629,7 +649,7 @@ bool Browser::match(const Entry &entry) const
case All:
break;
case Queued:
- if(!entry.target)
+ if(!m_actions.count(const_cast<Entry *>(&entry)))
return false;
break;
case Installed:
@@ -723,7 +743,7 @@ void Browser::installVersion(const int index, const size_t verIndex)
const Version *target = entry->package->version(verIndex);
if(target == entry->current)
- resetAction(index);
+ resetTarget(index);
else
setTarget(index, target);
}
@@ -736,25 +756,25 @@ void Browser::uninstall(const int index, const bool toggle)
setTarget(index, nullptr, toggle);
}
-void Browser::resetAction(const int index)
+void Browser::togglePin(const int index)
{
Entry *entry = getEntry(index);
- if(!entry->target)
+ if(!entry)
return;
- entry->target = boost::none;
- m_actions.erase(entry);
+ const bool newVal = !entry->pin.value_or(entry->regEntry.pinned);
- if(currentTab() == Queued) {
- m_list->removeRow(index);
- m_visibleEntries.erase(m_visibleEntries.begin() + index);
- updateDisplayLabel();
+ if(newVal == entry->regEntry.pinned) {
+ entry->pin = boost::none;
+ if(!entry->target)
+ m_actions.erase(entry);
+ }
+ else {
+ entry->pin = newVal;
+ m_actions.insert(entry);
}
- else
- m_list->replaceRow(index, makeRow(*entry));
- if(m_actions.empty())
- disable(m_apply);
+ updateAction(index);
}
void Browser::setTarget(const int index, const Version *target, const bool toggle)
@@ -762,13 +782,45 @@ void Browser::setTarget(const int index, const Version *target, const bool toggl
Entry *entry = getEntry(index);
if(toggle && entry->target && *entry->target == target)
- resetAction(index);
+ resetTarget(index);
else if(entry) {
entry->target = target;
m_actions.insert(entry);
+ updateAction(index);
+ }
+}
+
+void Browser::resetTarget(const int index)
+{
+ Entry *entry = getEntry(index);
+ if(!entry->target)
+ return;
+
+ entry->target = boost::none;
+ if(!entry->pin || !entry->canPin())
+ m_actions.erase(entry);
+
+ updateAction(index);
+}
+
+void Browser::updateAction(const int index)
+{
+ Entry *entry = getEntry(index);
+ if(!entry)
+ return;
+
+ if(currentTab() == Queued && !m_actions.count(entry)) {
+ m_list->removeRow(index);
+ m_visibleEntries.erase(m_visibleEntries.begin() + index);
+ updateDisplayLabel();
+ }
+ else
m_list->replaceRow(index, makeRow(*entry));
+
+ if(m_actions.empty())
+ disable(m_apply);
+ else
enable(m_apply);
- }
}
void Browser::selectionDo(const function<void (int)> &func)
@@ -823,18 +875,21 @@ bool Browser::apply()
return false;
for(Entry *entry : m_actions) {
- if(!entry->target)
- continue;
+ if(entry->target) {
+ const Version *target = *entry->target;
- const Version *target = *entry->target;
+ if(target)
+ tx->install(target);
+ else
+ tx->uninstall(entry->regEntry);
- if(target)
- tx->install(target);
- else
- tx->uninstall(entry->regEntry);
+ entry->target = boost::none;
+ }
- // clear queued actions
- entry->target = boost::none;
+ if(entry->pin) {
+ tx->setPinned(entry->package, *entry->pin);
+ entry->pin = boost::none;
+ }
}
m_actions.clear();
diff --git a/src/browser.hpp b/src/browser.hpp
@@ -61,15 +61,19 @@ private:
struct Entry {
typedef std::tuple<std::string, std::string, std::string> Hash;
+
int flags;
Registry::Entry regEntry;
const Package *package;
const Version *latest;
const Version *current;
+
boost::optional<const Version *> target;
+ boost::optional<bool> pin;
Hash hash() const;
bool test(Flag f) const { return (flags & f) != 0; }
+ bool canPin() const { return target ? *target != nullptr : test(InstalledFlag); }
bool operator==(const Entry &o) const { return hash() == o.hash(); }
};
@@ -107,6 +111,8 @@ private:
bool isFiltered(Package::Type) const;
void toggleFiltered(Package::Type);
void setTarget(const int index, const Version *, bool toggle = true);
+ void resetTarget(int index);
+ void updateAction(const int index);
void selectionDo(const std::function<void (int)> &);
Tab currentTab() const;
bool confirm() const;
@@ -116,7 +122,7 @@ private:
void reinstall(int index, bool toggle = true);
void installVersion(int index, size_t verIndex);
void uninstall(int index, bool toggle = true);
- void resetAction(int index);
+ void togglePin(int index);
void history(int index);
void contents(int index);
void about(int index);
diff --git a/src/receipt.hpp b/src/receipt.hpp
@@ -22,8 +22,17 @@
#include <string>
#include <vector>
+#include "registry.hpp"
+
class Path;
-struct InstallTicket;
+
+struct InstallTicket {
+ enum Type { Install, Upgrade };
+
+ Type type;
+ const Version *version;
+ Registry::Entry regEntry;
+};
class Receipt {
public:
diff --git a/src/registry.cpp b/src/registry.cpp
@@ -42,14 +42,15 @@ Registry::Registry(const Path &path)
"UPDATE entries SET type = ?, version = ?, author = ? WHERE id = ?"
);
+ m_setPinned = m_db.prepare("UPDATE entries SET pinned = ? WHERE id = ?");
+
m_findEntry = m_db.prepare(
- "SELECT id, remote, category, package, type, version, author FROM entries "
- "WHERE remote = ? AND category = ? AND package = ? "
- "LIMIT 1"
+ "SELECT id, remote, category, package, type, version, author, pinned "
+ "FROM entries WHERE remote = ? AND category = ? AND package = ? LIMIT 1"
);
m_allEntries = m_db.prepare(
- "SELECT id, category, package, type, version, author "
+ "SELECT id, category, package, type, version, author, pinned "
"FROM entries WHERE remote = ?"
);
m_forgetEntry = m_db.prepare("DELETE FROM entries WHERE id = ?");
@@ -92,6 +93,7 @@ void Registry::migrate()
" type INTEGER NOT NULL,"
" version TEXT NOT NULL,"
" author TEXT NOT NULL,"
+ " pinned INTEGER DEFAULT 0,"
" UNIQUE(remote, category, package)"
");"
@@ -188,6 +190,13 @@ auto Registry::push(const Version *ver, vector<Path> *conflicts) -> Entry
}
}
+void Registry::setPinned(const Entry &entry, const bool pinned)
+{
+ m_setPinned->bind(1, pinned);
+ m_setPinned->bind(2, entry.id);
+ m_setPinned->exec();
+}
+
auto Registry::getEntry(const Package *pkg) const -> Entry
{
Entry entry{};
@@ -209,6 +218,7 @@ auto Registry::getEntry(const Package *pkg) const -> Entry
entry.type = static_cast<Package::Type>(m_findEntry->intColumn(col++));
entry.version.tryParse(m_findEntry->stringColumn(col++));
entry.version.setAuthor(m_findEntry->stringColumn(col++));
+ entry.pinned = m_findEntry->intColumn(col++) != 0;
return false;
});
@@ -232,6 +242,7 @@ auto Registry::getEntries(const string &remoteName) const -> vector<Entry>
entry.type = static_cast<Package::Type>(m_allEntries->intColumn(col++));
entry.version.tryParse(m_allEntries->stringColumn(col++));
entry.version.setAuthor(m_allEntries->stringColumn(col++));
+ entry.pinned = m_allEntries->intColumn(col++) != 0;
list.push_back(entry);
diff --git a/src/registry.hpp b/src/registry.hpp
@@ -39,6 +39,7 @@ public:
std::string package;
Package::Type type;
Version version;
+ bool pinned;
operator bool() const { return id != 0; }
};
@@ -50,6 +51,7 @@ public:
std::set<Path> getFiles(const Entry &) const;
std::string getMainFile(const Entry &) const;
Entry push(const Version *, std::vector<Path> *conflicts = nullptr);
+ void setPinned(const Entry &, bool pinned);
void forget(const Entry &);
void savepoint();
void restore();
@@ -62,13 +64,14 @@ private:
Database m_db;
Statement *m_insertEntry;
Statement *m_updateEntry;
+ Statement *m_setPinned;
Statement *m_findEntry;
Statement *m_allEntries;
Statement *m_forgetEntry;
- Statement *m_setMainFile;
Statement *m_getFiles;
Statement *m_getMainFile;
+ Statement *m_setMainFile;
Statement *m_insertFile;
Statement *m_clearFiles;
Statement *m_forgetFiles;
diff --git a/src/task.hpp b/src/task.hpp
@@ -95,4 +95,14 @@ private:
std::set<Path> m_removedFiles;
};
+class DummyTask : public Task {
+public:
+ DummyTask(Transaction *t) : Task(t) {}
+
+protected:
+ void doStart() override {}
+ bool doCommit() override { return true; }
+ void doRollback() override {}
+};
+
#endif
diff --git a/src/transaction.cpp b/src/transaction.cpp
@@ -49,8 +49,9 @@ Transaction::Transaction()
finish();
});
+ // run tasks after fetching indexes
m_downloadQueue.onDone([=] {
- if(m_installQueue.empty())
+ if(m_tasks.empty())
finish();
else
runTasks();
@@ -105,7 +106,7 @@ void Transaction::synchronize(const Package *pkg, const InstallOpts &opts)
if(allFilesExists(latest->files()))
return; // latest version is really installed, nothing to do here!
}
- else if(regEntry.version > *latest)
+ else if(regEntry.pinned || *latest < regEntry.version)
return;
install(latest, regEntry);
@@ -186,18 +187,12 @@ void Transaction::install(const Version *ver,
if(!m_indexes.count(ri))
m_indexes.insert(ri);
- m_installQueue.push({type, ver, regEntry});
-}
-
-void Transaction::installTicket(const InstallTicket &ticket)
-{
- const Version *ver = ticket.version;
- const set<Path> ¤tFiles = m_registry->getFiles(ticket.regEntry);
+ const set<Path> ¤tFiles = m_registry->getFiles(regEntry);
InstallTask *task = new InstallTask(ver, currentFiles, this);
task->onCommit([=] {
- m_receipt.addTicket(ticket);
+ m_receipt.addTicket({type, ver, regEntry});
m_receipt.addRemovals(task->removedFiles());
const Registry::Entry newEntry = m_registry->push(ver);
@@ -209,6 +204,7 @@ void Transaction::installTicket(const InstallTicket &ticket)
});
addTask(task);
+ m_receipt.setEnabled(true);
}
void Transaction::registerAll(const Remote &remote)
@@ -222,6 +218,21 @@ void Transaction::registerAll(const Remote &remote)
inhibit(remote);
}
+void Transaction::setPinned(const Package *pkg, const bool pinned)
+{
+ // pkg may or may not be installed yet at this point,
+ // waiting for the install task to be commited before querying the registry
+
+ DummyTask *task = new DummyTask(this);
+ task->onCommit([=] {
+ const Registry::Entry &entry = m_registry->getEntry(pkg);
+ if(entry)
+ m_registry->setPinned(entry, pinned);
+ });
+
+ addTask(task);
+}
+
void Transaction::uninstall(const Remote &remote)
{
inhibit(remote);
@@ -261,6 +272,7 @@ void Transaction::uninstall(const Registry::Entry &entry)
task->onCommit([=] { m_receipt.addRemovals(task->removedFiles()); });
addTask(task);
+ m_receipt.setEnabled(true);
}
bool Transaction::saveFile(Download *dl, const Path &path)
@@ -315,8 +327,6 @@ void Transaction::addTask(Task *task)
{
m_tasks.push_back(task);
m_taskQueue.push(task);
-
- m_receipt.setEnabled(true);
}
bool Transaction::runTasks()
@@ -327,11 +337,6 @@ bool Transaction::runTasks()
m_onRun();
m_onRun.disconnect_all_slots();
- while(!m_installQueue.empty()) {
- installTicket(m_installQueue.front());
- m_installQueue.pop();
- }
-
while(!m_taskQueue.empty()) {
m_taskQueue.front()->start();
m_taskQueue.pop();
diff --git a/src/transaction.hpp b/src/transaction.hpp
@@ -36,14 +36,6 @@ struct InstallOpts;
typedef std::shared_ptr<const Index> IndexPtr;
-struct InstallTicket {
- enum Type { Install, Upgrade };
-
- Type type;
- const Version *version;
- const Registry::Entry regEntry;
-};
-
struct HostTicket { bool add; Registry::Entry entry; std::string file; };
class Transaction {
@@ -59,6 +51,7 @@ public:
void synchronize(const Remote &, const InstallOpts &);
void install(const Version *);
+ void setPinned(const Package *, bool pinned);
void uninstall(const Remote &);
void uninstall(const Registry::Entry &);
void registerAll(const Remote &);
@@ -81,7 +74,6 @@ private:
void synchronize(const Package *, const InstallOpts &);
void install(const Version *, const Registry::Entry &);
- void installTicket(const InstallTicket &);
void addTask(Task *);
bool allFilesExists(const std::set<Path> &) const;
@@ -104,7 +96,6 @@ private:
DownloadQueue m_downloadQueue;
std::queue<Task *> m_taskQueue;
- std::queue<InstallTicket> m_installQueue;
std::queue<HostTicket> m_regQueue;
VoidSignal m_onRun;
diff --git a/test/registry.cpp b/test/registry.cpp
@@ -174,3 +174,20 @@ TEST_CASE("get main file", M) {
const Registry::Entry &entry = reg.push(ver);
REQUIRE(reg.getMainFile(entry) == main->targetPath().join('/'));
}
+
+TEST_CASE("pin registry entry", M) {
+ MAKE_PACKAGE
+
+ Registry reg;
+ reg.push(ver);
+
+ const Registry::Entry &entry = reg.getEntry(&pkg);
+ REQUIRE_FALSE(entry.pinned);
+
+ reg.setPinned(entry, true);
+ REQUIRE(reg.getEntry(&pkg).pinned);
+ REQUIRE(reg.getEntries(ri.name())[0].pinned);
+
+ reg.setPinned(entry, false);
+ REQUIRE_FALSE(reg.getEntry(&pkg).pinned);
+}