ferdiOS 3.0 ships with a rebuilt package manager
After eighteen months of work, the distribution's third major release replaces the venerable fpkg tool with a transactional installer that can roll back a broken upgrade without leaving the shell.
The release everyone has been waiting on landed this morning, a little over eighteen months after the 2.x series was declared feature-frozen. ferdiOS 3.0 is, by the release team’s own description, “a boring release with one enormous exception” – and that exception is the package manager.
What changed
fpkg has been rewritten from the ground up. The old tool unpacked archives directly
into the live filesystem, which made a failed upgrade a genuinely frightening event. The
new implementation stages every transaction first and swaps it into place only once every
package in the set has been verified.
# roll the whole system forward, atomically
fpkg upgrade --all
# and back again, if the kernel decides otherwise
fpkg rollback
The practical effect is that an interrupted upgrade is no longer an interrupted system.
Under the hood
The staging logic is a few hundred lines of C++ sitting between the solver and the filesystem. A transaction owns its staging directory and, unless it is explicitly committed, throws the whole thing away when it goes out of scope:
#include <filesystem>
#include <vector>
namespace fpkg {
class Transaction {
public:
explicit Transaction(std::filesystem::path root)
: root_(std::move(root)),
staging_(root_ / ".fpkg" / "staging") {
std::filesystem::create_directories(staging_);
}
// A transaction that is never committed leaves nothing behind.
~Transaction() {
if (!committed_) {
std::error_code ec;
std::filesystem::remove_all(staging_, ec);
}
}
Transaction(const Transaction&) = delete;
Transaction& operator=(const Transaction&) = delete;
void stage(const Package& pkg) {
pkg.unpack_into(staging_);
staged_.push_back(pkg.name());
}
bool commit() {
for (const auto& name : staged_) {
if (!verify(staging_ / name)) {
return false; // destructor cleans up the whole set
}
}
std::filesystem::rename(staging_, root_ / "usr");
committed_ = true;
return true;
}
private:
std::filesystem::path root_;
std::filesystem::path staging_;
std::vector<std::string> staged_;
bool committed_ = false;
};
} // namespace fpkg
Every failure path ends in the same place: the staging directory is removed and the live system is exactly where it was.
We stopped trying to make failures rare and started making them cheap. That turned out to be the easier problem.
The rest of the release
Beyond the installer, 3.0 is deliberately quiet. The changes worth knowing about:
- The default toolchain moves to a current LLVM, with GCC still available as a side-by-side option.
- Service startup is roughly forty percent faster on the reference hardware, almost entirely from removing serialised filesystem checks.
- The installer image drops below 300 MB again after several releases of steady growth.
- Documentation now ships in the base image rather than as a separate package.
Upgrading
Existing 2.x systems can move across in place. The release team recommends taking the upgrade in a single transaction rather than package by package – the new tool is designed for it, and the piecemeal route is the only one that has produced bug reports during the beta.
Mirrors are carrying the images now. The signing keys are unchanged from 2.8.