Files
InstanceBrick--nightcap/include/update/apk_provider.h
T
LightZirconite 9da2c02836 Resolve APK providers through a replaceable chain
The coordinator constructed ApkPureProvider directly in three places and
rejected every other configuration with "native updater supports only the
direct APKPure provider". A single unreachable host therefore failed the
whole update with no way to try anything else, and the reported error named
neither the provider nor the host: on 2026-08-18 api.pureapk.com answered
503 to every request, including metadata for unrelated packages, and a
first launch could only say "Roblox update preflight failed".

Introduce an ApkProvider interface and a ProviderChain that consults
providers in order, returns the first answer, and reports every failure
with the provider that produced it when none answer. Each provider
downloads into its own directory, so a failed attempt cannot block the
next. Adding a source is now one line in BuildProviderChain instead of a
change spread across the coordinator.

The chain currently holds APKPure alone. The repository already ships a
pinned Uptodown provider as a build script, but it no longer works against
Uptodown's current pages, so wiring it in would add a second way to fail
rather than a fallback; that is reported separately.

Payload receipts now record which provider produced an archive, and
updates.source accepts auto alongside apk-pure so existing configuration
files keep working.
2026-08-19 10:41:11 +02:00

70 lines
2.1 KiB
C++

#ifndef MOCKTAIL_UPDATE_APK_PROVIDER_H_
#define MOCKTAIL_UPDATE_APK_PROVIDER_H_
#include <cstdint>
#include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace mocktail::update {
struct ProviderVersion {
std::string version_name;
std::uint64_t version_code = 0;
std::string error;
explicit operator bool() const { return error.empty(); }
};
struct ProviderDownloadResult {
std::vector<std::filesystem::path> archives;
// Provider that produced the archives, so a stored payload can be traced
// back to where it came from.
std::string source;
std::string error;
explicit operator bool() const { return error.empty(); }
};
// A source of Roblox APK archives. Providers are interchangeable: whichever
// one answers first wins, and every archive still passes the same signature,
// Build-ID, and ABI verification afterwards.
class ApkProvider {
public:
virtual ~ApkProvider() = default;
// Short stable identifier, recorded in payload receipts and error messages.
virtual std::string_view name() const = 0;
// Latest published version. A provider that only serves pinned versions
// reports an error here and stays usable through DownloadExact.
virtual ProviderVersion CheckLatest() const = 0;
virtual ProviderDownloadResult DownloadExact(
std::string_view version, const std::filesystem::path& output_directory,
int progress_fd) const = 0;
};
// Tries every provider in order. One unreachable provider is the most common
// first-run failure, and on its own it must not fail the whole update.
class ProviderChain final : public ApkProvider {
public:
void Add(std::unique_ptr<ApkProvider> provider);
bool empty() const { return providers_.empty(); }
std::string_view name() const override { return "provider-chain"; }
ProviderVersion CheckLatest() const override;
ProviderDownloadResult DownloadExact(
std::string_view version, const std::filesystem::path& output_directory,
int progress_fd) const override;
private:
std::vector<std::unique_ptr<ApkProvider>> providers_;
};
} // namespace mocktail::update
#endif // MOCKTAIL_UPDATE_APK_PROVIDER_H_