diff options
Diffstat (limited to 'bpkg/pkg-fetch.cxx')
-rw-r--r-- | bpkg/pkg-fetch.cxx | 112 |
1 files changed, 97 insertions, 15 deletions
diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx index ab67ffe..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> @@ -43,17 +44,24 @@ namespace bpkg // normalize (a, "archive"); + // 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); - shared_ptr<selected_package> p (db.find<selected_package> (n)); 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 (db, 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 @@ -94,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. @@ -162,7 +171,9 @@ 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;}); @@ -193,6 +204,8 @@ namespace bpkg bool replace, bool simulate) { + assert (session::has_current ()); + tracer trace ("pkg_fetch"); tracer_guard tg (pdb, trace); // NOTE: sets tracer for the whole cluster. @@ -221,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.local ()) - break; + if (rl.archive_based () && (pl == nullptr || rl.local ())) + { + pl = &l; + + if (rl.local ()) + break; + } } } @@ -241,10 +257,76 @@ namespace bpkg << "from " << pl->repository_fragment->name; auto_rmfile arm; - path a (pdb.config_orig / 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); @@ -254,12 +336,12 @@ 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"; |