diff --git a/NEWS b/NEWS
index ac10d540..8901ff0e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,10 @@
xbps-0.44 (???):
+ * xbps-pkgdb(8): added a new mode to set in installed packages: "repolock".
+ If that mode is set with `-m repo(un)lock`, a package will only be updated
+ if there's an update in the same repository that was used for installing.
+ This implements #77 (https://github.com/voidlinux/xbps/issues/77)
+
* libxbps: properly detect when a file has been moved between packages:
if the mtime stored in the pkg metadata does not match the mtime
of the same file stored on disk, don't detect this as an obsolete file.
diff --git a/bin/xbps-pkgdb/main.c b/bin/xbps-pkgdb/main.c
index c0b8e19e..ee1eff34 100644
--- a/bin/xbps-pkgdb/main.c
+++ b/bin/xbps-pkgdb/main.c
@@ -44,7 +44,8 @@ usage(bool fail)
" -C --config
Path to confdir (xbps.d)\n"
" -d --debug Debug mode shown to stderr\n"
" -h --help Print usage help\n"
- " -m --mode Change PKGNAME to this mode\n"
+ " -m --mode \n"
+ " Change PKGNAME to this mode\n"
" -r --rootdir Full path to rootdir\n"
" -u --update Update pkgdb to the latest format\n"
" -v --verbose Verbose messages\n"
@@ -69,6 +70,10 @@ change_pkg_mode(struct xbps_handle *xhp, const char *pkgname, const char *mode)
xbps_dictionary_set_bool(pkgd, "hold", true);
else if (strcmp(mode, "unhold") == 0)
xbps_dictionary_remove(pkgd, "hold");
+ else if (strcmp(mode, "repolock") == 0)
+ xbps_dictionary_set_bool(pkgd, "repolock", true);
+ else if (strcmp(mode, "repounlock") == 0)
+ xbps_dictionary_remove(pkgd, "repolock");
else
usage(true);
diff --git a/bin/xbps-pkgdb/xbps-pkgdb.8 b/bin/xbps-pkgdb/xbps-pkgdb.8
index 1d76a6b6..bc421c8b 100644
--- a/bin/xbps-pkgdb/xbps-pkgdb.8
+++ b/bin/xbps-pkgdb/xbps-pkgdb.8
@@ -1,4 +1,4 @@
-.Dd February 22, 2014
+.Dd March 3, 2014
.Dt XBPS-PKGDB 8
.Sh NAME
.Nm xbps-pkgdb
@@ -47,10 +47,11 @@ If the first character is not '\/' then it's a relative path of
Enables extra debugging shown to stderr.
.It Fl h, Fl -help
Show the help usage.
-.It Fl m, Fl -mode Ar auto|manual|hold|unhold
+.It Fl m, Fl -mode Ar auto|manual|hold|unhold|repolock|repounlock
Switches
.Ar PKGNAME
-to the specified mode: automatic or manual installation mode, or to (un)set it on hold mode.
+to the specified mode: automatic or manual installation mode, or to (un)set the on hold mode,
+and repository locked mode.
A package that was installed as dependency will be in
.Sy automatic
mode, otherwise will be set to
@@ -60,6 +61,10 @@ A package in
mode won't be updated in full system upgrades. The list of packages in this mode can be
seen with
.Xr xbps-query 8 .
+A package in
+.Sy repolock
+mode will only accept updates that are available in the same repository that was used
+for installing.
.It Fl r, Fl -rootdir Ar dir
Specifies a full path for the target root directory.
.It Fl u, Fl -update
diff --git a/lib/transaction_ops.c b/lib/transaction_ops.c
index 35d1ec65..93f8874d 100644
--- a/lib/transaction_ops.c
+++ b/lib/transaction_ops.c
@@ -68,7 +68,7 @@ trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall,
char *pkgname;
int action = 0, rv = 0;
pkg_state_t state = 0;
- bool autoinst = false;
+ bool autoinst = false, repolock = false;
assert(pkg != NULL);
@@ -102,7 +102,20 @@ trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall,
action = TRANS_REINSTALL;
reason = "install";
}
- if ((pkg_repod = xbps_rpool_get_pkg(xhp, pkg)) == NULL) {
+ xbps_dictionary_get_bool(pkg_pkgdb, "repolock", &repolock);
+ if (repolock) {
+ struct xbps_repo *repo;
+ /* find update from repo */
+ xbps_dictionary_get_cstring_nocopy(pkg_pkgdb, "repository", &repoloc);
+ assert(repoloc);
+ if ((repo = xbps_rpool_get_repo(repoloc)) == NULL)
+ return EINVAL;
+ pkg_repod = xbps_repo_get_pkg(repo, pkg);
+ } else {
+ /* find update from rpool */
+ pkg_repod = xbps_rpool_get_pkg(xhp, pkg);
+ }
+ if (pkg_repod == NULL) {
/* not found */
return ENOENT;
}
@@ -141,11 +154,12 @@ trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall,
if (pkg_pkgdb) {
/*
- * If pkg is already installed, respect its automatic-install
- * property.
+ * If pkg is already installed, respect some properties.
*/
if (xbps_dictionary_get_bool(pkg_pkgdb, "automatic-install", &autoinst))
xbps_dictionary_set_bool(pkg_repod, "automatic-install", autoinst);
+ if (xbps_dictionary_get_bool(pkg_pkgdb, "repolock", &repolock))
+ xbps_dictionary_set_bool(pkg_repod, "repolock", repolock);
}
/*
* Prepare transaction dictionary.
diff --git a/tests/xbps/libxbps/shell/Kyuafile b/tests/xbps/libxbps/shell/Kyuafile
index 9b48c916..29203594 100644
--- a/tests/xbps/libxbps/shell/Kyuafile
+++ b/tests/xbps/libxbps/shell/Kyuafile
@@ -18,3 +18,4 @@ atf_test_program{name="install_test"}
atf_test_program{name="preserve_files_test"}
atf_test_program{name="update_shlibs"}
atf_test_program{name="update_hold"}
+atf_test_program{name="update_repolock"}
diff --git a/tests/xbps/libxbps/shell/Makefile b/tests/xbps/libxbps/shell/Makefile
index 46586503..0b191262 100644
--- a/tests/xbps/libxbps/shell/Makefile
+++ b/tests/xbps/libxbps/shell/Makefile
@@ -6,7 +6,7 @@ TESTSHELL = conf_files_test issue6_test issue18_test issue20_test remove_test
TESTSHELL+= replace_test installmode_test obsoletefiles_test
TESTSHELL+= issue31_test scripts_test incorrect_deps_test
TESTSHELL+= vpkg_test install_test preserve_files_test
-TESTSHELL+= update_shlibs update_hold
+TESTSHELL+= update_shlibs update_hold update_repolock
EXTRA_FILES = Kyuafile
include $(TOPDIR)/mk/test.mk
diff --git a/tests/xbps/libxbps/shell/update_repolock.sh b/tests/xbps/libxbps/shell/update_repolock.sh
new file mode 100644
index 00000000..c5640ddf
--- /dev/null
+++ b/tests/xbps/libxbps/shell/update_repolock.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env atf-sh
+
+atf_test_case update_repolock
+
+update_repolock_head() {
+ atf_set "descr" "Tests for pkg update: pkg is in repository locked mode"
+}
+
+update_repolock_body() {
+ mkdir -p repo repo2 pkg_A
+ cd repo
+ xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ../repo2
+ xbps-create -A noarch -n A-1.1_1 -s "A pkg" ../pkg_A
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+ # install A-1.0_1 from repository "repo"
+ xbps-install -r root --repository=repo --repository=repo2 -yvd A-1.0_1
+ atf_check_equal $? 0
+ # A-1.0_1 is now locked
+ xbps-pkgdb -r root -m repolock A
+ atf_check_equal $? 0
+ out=$(xbps-query -r root -p repository A)
+ atf_check_equal "$out" "$(realpath repo)"
+ # no update due to repository locking
+ xbps-install -r root --repository=repo --repository=repo2 -yuvd
+ atf_check_equal $? 0
+ out=$(xbps-query -r root -p pkgver A)
+ atf_check_equal $out A-1.0_1
+ # disable repolock
+ xbps-pkgdb -r root -m repounlock A
+ atf_check_equal $? 0
+ out=$(xbps-query -r root -p repolock A)
+ atf_check_equal "$out" ""
+ # update A to 1.1_1 from repo2
+ xbps-install -r root --repository=repo2 --repository=repo -yuvd
+ atf_check_equal $? 0
+ out=$(xbps-query -r root -p pkgver A)
+ atf_check_equal $out A-1.1_1
+ out=$(xbps-query -r root -p repository A)
+ atf_check_equal "$out" "$(realpath repo2)"
+}
+
+atf_init_test_cases() {
+ atf_add_test_case update_repolock
+}