Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion conan/internal/api/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from conan.internal.errors import NotFoundException
from conan.errors import ConanException
from conan.internal.paths import CONAN_MANIFEST, CONANFILE, CONANINFO, COMPRESSIONS, \
EXPORT_SOURCES_FILE_NAME, EXPORT_FILE_NAME, PACKAGE_FILE_NAME
EXPORT_SOURCES_FILE_NAME, EXPORT_FILE_NAME, PACKAGE_FILE_NAME, CONAN_METADATA_SUBFOLDER
from conan.internal.util.files import (clean_dirty, is_dirty, gather_files,
set_dirty_context_manager, mkdir, human_size)

Expand Down Expand Up @@ -133,6 +133,9 @@ def prepare(self, pkg_list, enabled_remotes, metadata, force=False):
bundle.pop("upload-urls", None)
if bundle.get("upload") or force:
self._prepare_recipe(recipe_layout, ref, bundle, conanfile, enabled_remotes)
conan_files = _conan_metadata_files(recipe_layout.metadata())
if conan_files:
bundle.setdefault("files", {}).update(conan_files)

# Package metadata files too
if metadata != [""] and (metadata or bundle.get("upload")):
Expand Down Expand Up @@ -377,6 +380,21 @@ def _total_size(cache_files):
return human_size(total_size)


def _conan_metadata_files(metadata_folder):
"""Collect files from metadata/.conan subfolder for automatic upload with the recipe."""
conan_subfolder = os.path.join(metadata_folder, CONAN_METADATA_SUBFOLDER)
result = {}
if not os.path.isdir(conan_subfolder):
return result
for root, _, files in os.walk(conan_subfolder):
for f in files:
abs_path = os.path.join(root, f)
relpath = os.path.relpath(abs_path, metadata_folder)
path = os.path.join("metadata", relpath).replace("\\", "/")
result[path] = abs_path
return result


def _metadata_files(folder, metadata):
result = {}
for root, _, files in os.walk(folder):
Expand Down
1 change: 1 addition & 0 deletions conan/internal/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,4 @@ def _user_home_from_conanrc_file():
EXPORT_SOURCES_FILE_NAME = "conan_sources.t"
COMPRESSIONS = "gz", "xz", "zst"
DATA_YML = "conandata.yml"
CONAN_METADATA_SUBFOLDER = ".conan"
5 changes: 3 additions & 2 deletions conan/internal/rest/rest_client_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from conan.api.output import ConanOutput
from conan.internal.paths import EXPORT_SOURCES_FILE_NAME, CONANINFO, CONAN_MANIFEST, \
EXPORT_FILE_NAME, PACKAGE_FILE_NAME
EXPORT_FILE_NAME, PACKAGE_FILE_NAME, CONAN_METADATA_SUBFOLDER
from conan.internal.rest.caching_file_downloader import ConanInternalCacheDownloader
from conan.internal.rest import response_to_str
from conan.internal.rest.client_routes import ClientV2Router
Expand Down Expand Up @@ -212,7 +212,8 @@ def get_recipe(self, ref, dest_folder, metadata, only_metadata):
result = {}

if not only_metadata:
accepted_files = ["conanfile.py", CONAN_MANIFEST, "metadata/sign"]
accepted_files = ["conanfile.py", CONAN_MANIFEST, "metadata/sign",
f"metadata/{CONAN_METADATA_SUBFOLDER}"]
files = [f for f in server_files if any(f.startswith(m) for m in accepted_files)]
export_file = self._find_compressed_file(ref, server_files, EXPORT_FILE_NAME)
if export_file is not None:
Expand Down
47 changes: 47 additions & 0 deletions test/integration/metadata/test_metadata_conan_subfolder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import os
import pytest

from conan.test.assets.genconanfile import GenConanfile
from conan.test.utils.tools import TestClient
from conan.internal.util.files import save, load


class TestConanMetadataSubfolder:

@pytest.fixture()
def uploaded_pkg(self):
"""Create and upload pkg/0.1 with metadata/.conan/info.txt, no --metadata flag."""
c = TestClient(default_server_user=True, light=True)
c.save({"conanfile.py": GenConanfile("pkg", "0.1")})
c.run("create .")
c.run("cache path pkg/0.1 --folder=metadata")
metadata_path = str(c.stdout).strip()
save(os.path.join(metadata_path, ".conan", "info.txt"), "conan metadata content")
c.run("upload * -c -r=default")
return c

def test_create_upload_install(self, uploaded_pkg):
"""metadata/.conan is always uploaded with recipe and always downloaded on install."""
c2 = TestClient(servers=uploaded_pkg.servers, light=True)
c2.run("install --requires=pkg/0.1")
c2.run("cache path pkg/0.1 --folder=metadata")
metadata_path = str(c2.stdout).strip()
assert load(os.path.join(metadata_path, ".conan", "info.txt")) == "conan metadata content"

def test_download_irrespective_of_metadata_filter(self, uploaded_pkg):
"""conan download always gets metadata/.conan regardless of --metadata filter."""
c2 = TestClient(servers=uploaded_pkg.servers, light=True)

# Without --metadata flag: .conan is still fetched
c2.run("download pkg/0.1 -r=default")
c2.run("cache path pkg/0.1 --folder=metadata")
metadata_path = str(c2.stdout).strip()
assert load(os.path.join(metadata_path, ".conan", "info.txt")) == "conan metadata content"

c2.run("remove * -c")

# With --metadata filtering other patterns: .conan subfolder still fetched
c2.run("download pkg/0.1 -r=default --metadata=other/*")
c2.run("cache path pkg/0.1 --folder=metadata")
metadata_path = str(c2.stdout).strip()
assert load(os.path.join(metadata_path, ".conan", "info.txt")) == "conan metadata content"
Loading