diff --git a/bob/devtools/ci.py b/bob/devtools/ci.py
index 30098d40eae818c7be064e60743b8c56395eb90b..186d767512987c3ebfad06685e357436537a3da2 100644
--- a/bob/devtools/ci.py
+++ b/bob/devtools/ci.py
@@ -40,6 +40,43 @@ def is_master(refname, tag, repodir):
     return refname == "master"
 
 
+def is_private(baseurl, package):
+    """Tells if a given package (with a namespace) is public or private
+
+    This function checks if a fully qualified package in the format
+    ``<namespace>/<name>`` is publicly accessible.  It does this by trying to
+    access ``info/refs?service=git-upload-pack`` from the package in question.
+
+    This method does **not** rely on the fact the user has access to Gitlab.
+
+    .. warning::
+
+       This method only works for fully qualified package names (i.e.,
+       containing at least one forward-slash ``/``).
+
+    Args:
+
+      baseurl: The base URL for the gitlab service to consult
+      package: Fully qualified (i.e., with a namespace) package name.  For
+        example: ``bob/bob.extension``.
+
+    Returns: a boolean, indicating if the package is private (``True``) or not
+    (``False``).
+    """
+
+    from urllib.error import HTTPError
+    from urllib.request import urlopen
+
+    private = True
+    try:
+        r = urlopen(baseurl + "/" + package + "/info/refs?service=git-upload-pack")
+        private = r.getcode() != 200
+    except HTTPError as e:
+        private = e.getcode() == 401
+
+    return private
+
+
 def is_stable(package, refname, tag, repodir):
     """Determines if the package being published is stable.
 
diff --git a/bob/devtools/scripts/ci.py b/bob/devtools/scripts/ci.py
index 4bfde7d27eebca8312d830faadcd63a08bcce08d..9a7686962913cd40d8b3eee5b4fe0d8ffddc2c16 100644
--- a/bob/devtools/scripts/ci.py
+++ b/bob/devtools/scripts/ci.py
@@ -14,6 +14,7 @@ from click_plugins import with_plugins
 from ..build import comment_cleanup
 from ..build import load_order_file
 from ..ci import cleanup
+from ..ci import is_private
 from ..ci import read_packages
 from ..ci import select_conda_build_config
 from ..ci import select_conda_recipe_append
@@ -667,8 +668,6 @@ def nightlies(ctx, order, dry_run):
 
     token = os.environ["CI_JOB_TOKEN"]
 
-    from urllib.request import urlopen
-
     import git
 
     from .build import build
@@ -698,7 +697,10 @@ def nightlies(ctx, order, dry_run):
         )
 
         # determine package visibility
-        private = urlopen("https://gitlab.idiap.ch/%s" % package).getcode() != 200
+        private = is_private("https://gitlab.idiap.ch", package)
+        private_str = "PRIVATE" if private else "PUBLIC"
+        logger.info('Package "%s" is %s', package, private_str)
+
         stable = "STABLE" in os.environ
 
         # Use custom variants and append files if available on recipe-dir
diff --git a/bob/devtools/test_ci.py b/bob/devtools/test_ci.py
new file mode 100644
index 0000000000000000000000000000000000000000..43d4891bb080a2dbf07f3b1275f2a12d3bcb6ffa
--- /dev/null
+++ b/bob/devtools/test_ci.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+from .ci import is_private
+
+
+def test_is_private():
+
+    base_url = "https://gitlab.idiap.ch"
+    assert not is_private(base_url, "bob/bob.extension")
+    assert is_private(base_url, "bob/private")
diff --git a/conda/meta.yaml b/conda/meta.yaml
index 24d92eae90cd18e7d76db340cc17d5d6f5573167..8ee87fa4e6f7cf55f96b563a96a72ef8c41c73f2 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -55,6 +55,8 @@ requirements:
 test:
   requires:
     - sphinx_rtd_theme
+    - pytest
+    - pytest-cov
   imports:
     - {{ name }}
   commands:
@@ -113,6 +115,7 @@ test:
     - bdt sphinx --help
     - bdt sphinx migrate-autodoc-flags --help
     - bdt gitlab alt-nightlies --help
+    - pytest --capture=no --verbose --cov {{ name }} --cov-report term-missing --cov-report html:{{ project_dir }}/sphinx/coverage --cov-report xml:{{ project_dir }}/coverage.xml --pyargs {{ name }}
     - sphinx-build -aEW ${PREFIX}/share/doc/{{ name }}/doc sphinx
     {% if not os.path.exists('sphinx') %}
     - if [ -n "${CI_PROJECT_DIR}" ]; then mv sphinx "${CI_PROJECT_DIR}/"; fi