aboutsummaryrefslogtreecommitdiff
path: root/bpkg/pkg-fetch.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/pkg-fetch.cxx')
-rw-r--r--bpkg/pkg-fetch.cxx112
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";