diff options
Diffstat (limited to 'bpkg/pkg-fetch.cxx')
-rw-r--r-- | bpkg/pkg-fetch.cxx | 163 |
1 files changed, 122 insertions, 41 deletions
diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx index 75bf1fa..837c968 100644 --- a/bpkg/pkg-fetch.cxx +++ b/bpkg/pkg-fetch.cxx @@ -10,6 +10,7 @@ #include <bpkg/package-odb.hxx> #include <bpkg/checksum.hxx> #include <bpkg/database.hxx> +#include <bpkg/rep-mask.hxx> #include <bpkg/diagnostics.hxx> #include <bpkg/manifest-utility.hxx> @@ -21,10 +22,10 @@ using namespace butl; namespace bpkg { - // Can return a new selected package object, replacing the existing one. + // Return the selected package object which may replace the existing one. // static shared_ptr<selected_package> - pkg_fetch (dir_path c, + pkg_fetch (database& db, transaction& t, package_name n, version v, @@ -35,27 +36,32 @@ namespace bpkg { tracer trace ("pkg_fetch"); - database& db (t.database ()); tracer_guard tg (db, trace); - // Make the archive and configuration paths absolute and normalized. - // If the archive is inside the configuration, use the relative path. - // This way we can move the configuration around. + // Make the archive path absolute and normalized. If the archive is + // inside the configuration, use the relative path. This way we can move + // the configuration around. // - normalize (c, "configuration"); normalize (a, "archive"); - if (a.sub (c)) - a = a.leaf (c); - + // Only purge the existing archive if its path differs from the new path. + // shared_ptr<selected_package> p (db.find<selected_package> (n)); + + bool purge_archive (p != nullptr && + p->archive && + p->effective_archive (db.config) != a); + + if (a.sub (db.config)) + a = a.leaf (db.config); + if (p != nullptr) { // Clean up the source directory and archive of the package we are - // replacing. Once this is done, there is no going back. If things - // go badly, we can't simply abort the transaction. + // replacing. Once this is done, there is no going back. If things go + // badly, we can't simply abort the transaction. // - pkg_purge_fs (c, t, p, simulate); + pkg_purge_fs (db, t, p, simulate, purge_archive); // Note that if the package name spelling changed then we need to update // it, to make sure that the subsequent commands don't fail and the @@ -96,6 +102,7 @@ namespace bpkg nullopt, // No source directory yet. false, nullopt, // No manifest checksum. + nullopt, // No buildfiles checksum. nullopt, // No output directory yet. {}}); // No prerequisites captured yet. @@ -113,14 +120,13 @@ namespace bpkg // or fetching one. // static void - pkg_fetch_check (const dir_path& c, - transaction& t, + pkg_fetch_check (database& db, + transaction&, const package_name& n, bool replace) { tracer trace ("pkg_fetch_check"); - database& db (t.database ()); tracer_guard tg (db, trace); if (shared_ptr<selected_package> p = db.find<selected_package> (n)) @@ -131,6 +137,7 @@ namespace bpkg if (!replace || !s) { diag_record dr (fail); + const dir_path& c (db.config_orig); dr << "package " << n << " already exists in configuration " << c << info << "version: " << p->version_string () @@ -145,7 +152,7 @@ namespace bpkg shared_ptr<selected_package> pkg_fetch (const common_options& co, - const dir_path& c, + database& db, transaction& t, path a, bool replace, @@ -164,18 +171,20 @@ namespace bpkg package_manifest m (pkg_verify (co, a, true /* ignore_unknown */, - false /* expand_values */)); + false /* ignore_toolchain */, + false /* expand_values */, + false /* load_buildfiles */)); l4 ([&]{trace << m.name << " " << m.version;}); // Check/diagnose an already existing package. // - pkg_fetch_check (c, t, m.name, replace); + pkg_fetch_check (db, t, m.name, replace); // Use the special root repository fragment as the repository fragment of // this package. // - return pkg_fetch (c, + return pkg_fetch (db, t, move (m.name), move (m.version), @@ -187,23 +196,25 @@ namespace bpkg shared_ptr<selected_package> pkg_fetch (const common_options& co, - const dir_path& c, + database& pdb, + database& rdb, transaction& t, package_name n, version v, bool replace, bool simulate) { + assert (session::has_current ()); + tracer trace ("pkg_fetch"); - database& db (t.database ()); - tracer_guard tg (db, trace); + tracer_guard tg (pdb, trace); // NOTE: sets tracer for the whole cluster. // Check/diagnose an already existing package. // - pkg_fetch_check (c, t, n, replace); + pkg_fetch_check (pdb, t, n, replace); - check_any_available (c, t); + check_any_available (rdb, t); // Note that here we compare including the revision (unlike, say in // pkg-status). Which means one cannot just specify 1.0.0 and get 1.0.0+1 @@ -211,7 +222,7 @@ namespace bpkg // a low-level command where some extra precision doesn't hurt. // shared_ptr<available_package> ap ( - db.find<available_package> (available_package_id (n, v))); + rdb.find<available_package> (available_package_id (n, v))); if (ap == nullptr) fail << "package " << n << " " << v << " is not available"; @@ -223,14 +234,17 @@ namespace bpkg for (const package_location& l: ap->locations) { - const repository_location& rl (l.repository_fragment.load ()->location); - - if (rl.archive_based () && (pl == nullptr || rl.local ())) + if (!rep_masked_fragment (l.repository_fragment)) { - pl = &l; + const repository_location& rl (l.repository_fragment.load ()->location); + + if (rl.archive_based () && (pl == nullptr || rl.local ())) + { + pl = &l; - if (rl.local ()) - break; + if (rl.local ()) + break; + } } } @@ -243,10 +257,76 @@ namespace bpkg << "from " << pl->repository_fragment->name; auto_rmfile arm; - path a (c / pl->location.leaf ()); + path an (pl->location.leaf ()); + path a (pdb.config_orig / an); + + // Note that in the replace mode we first fetch the new package version + // archive and then update the existing selected package object, dropping + // the previous package version archive, if present. This way we, in + // particular, keep the existing selected package/archive intact if the + // fetch operation fails. However, this approach requires to handle + // re-fetching (potentially from a different repository) of the same + // package version specially. + // + // Specifically, if we need to overwrite the package archive file, then we + // stash the existing archive in the temporary directory and remove it on + // success. On failure, we try to move the stashed archive to the original + // place. Failed that either, we mark the package as broken. + // + // (If you are wondering why don't we instead always fetch into a + // temporary file, the answer is Windows, where moving a newly created + // file may not succeed because it is being scanned by Windows Defender + // or some such.) + // + auto_rmfile earm; + shared_ptr<selected_package> sp; + + auto g ( + make_exception_guard ( + [&arm, &a, &earm, &sp, &pdb, &t] () + { + // Restore stashed archive. + // + if (!earm.path.empty () && exists (earm.path)) + { + if (mv (earm.path, a, true /* ignore_error */)) + { + earm.cancel (); + arm.cancel (); // Note: may not be armed yet, which is ok. + } + // + // Note: can already be marked as broken by pkg_purge_fs(). + // + else if (sp->state != package_state::broken) + { + sp->state = package_state::broken; + pdb.update (sp); + t.commit (); + + // Here we assume that mv() has already issued the diagnostics. + // + info << "package " << sp->name << pdb << " is now broken; " + << "use 'pkg-purge --force' to remove"; + } + } + })); if (!simulate) { + // Stash the existing package archive if it needs to be overwritten (see + // above for details). + // + // Note: compare the archive absolute paths. + // + if (replace && + (sp = pdb.find<selected_package> (n)) != nullptr && + sp->archive && + sp->effective_archive (pdb.config) == pdb.config / an) + { + earm = tmp_file (pdb.config_orig, n.string () + '-' + v.string ()); + mv (a, earm.path); + } + pkg_fetch_archive ( co, pl->repository_fragment->location, pl->location, a); @@ -256,20 +336,20 @@ namespace bpkg // assert (ap->sha256sum); - const string& sha256sum (sha256 (co, a)); - if (sha256sum != *ap->sha256sum) + const string& cs (sha256sum (co, a)); + if (cs != *ap->sha256sum) { fail << "checksum mismatch for " << n << " " << v << info << pl->repository_fragment->name << " has " << *ap->sha256sum << - info << "fetched archive has " << sha256sum << + info << "fetched archive has " << cs << info << "consider re-fetching package list and trying again" << info << "if problem persists, consider reporting this to " - << "the repository maintainer"; + << "the repository maintainer"; } } shared_ptr<selected_package> p ( - pkg_fetch (c, + pkg_fetch (pdb, t, move (n), move (v), @@ -290,7 +370,7 @@ namespace bpkg dir_path c (o.directory ()); l4 ([&]{trace << "configuration: " << c;}); - database db (open (c, trace)); + database db (c, trace, true /* pre_attach */); transaction t (db); session s; @@ -305,7 +385,7 @@ namespace bpkg info << "run 'bpkg help pkg-fetch' for more information"; p = pkg_fetch (o, - c, + db, t, path (args.next ()), o.replace (), @@ -327,7 +407,8 @@ namespace bpkg info << "run 'bpkg help pkg-fetch' for more information"; p = pkg_fetch (o, - c, + db /* pdb */, + db /* rdb */, t, move (n), move (v), |