Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to specify revision_mode for each recipe #4767

Merged
merged 11 commits into from
Mar 25, 2019
41 changes: 28 additions & 13 deletions conans/client/cmd/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,23 @@


def export_alias(package_layout, target_ref, output, revisions_enabled):
revision_mode = "hash"
conanfile = """
from conans import ConanFile

class AliasConanfile(ConanFile):
alias = "%s"
""" % target_ref.full_repr()
revision_mode = "%s"
""" % (target_ref.full_repr(), revision_mode)

save(package_layout.conanfile(), conanfile)
digest = FileTreeManifest.create(package_layout.export())
digest.save(folder=package_layout.export())

# Create the metadata for the alias
_update_revision_in_metadata(package_layout=package_layout, revisions_enabled=revisions_enabled,
output=output, path=None, digest=digest)
output=output, path=None, digest=digest,
revision_mode=revision_mode)


def check_casing_conflict(cache, ref):
Expand Down Expand Up @@ -97,7 +100,8 @@ def cmd_export(package_layout, conanfile_path, conanfile, keep_source, revisions
revisions_enabled=revisions_enabled,
output=output,
path=os.path.dirname(conanfile_path),
digest=digest)
digest=digest,
revision_mode=conanfile.revision_mode)

# FIXME: Conan 2.0 Clear the registry entry if the recipe has changed
source_folder = package_layout.source()
Expand Down Expand Up @@ -239,19 +243,30 @@ def _detect_scm_revision(path):
return repo_obj.get_revision(), repo_type, repo_obj.is_pristine()


def _update_revision_in_metadata(package_layout, revisions_enabled, output, path, digest):
def _update_revision_in_metadata(package_layout, revisions_enabled, output, path, digest,
revision_mode):
if revision_mode not in ["scm", "hash"]:
raise ConanException("Revision mode should be one of 'hash' (default) or 'scm'")

scm_revision_detected, repo_type, is_pristine = _detect_scm_revision(path)
revision = scm_revision_detected or digest.summary_hash
if revisions_enabled:
if scm_revision_detected:
output.info("Using {} commit as the recipe"
" revision: {} ".format(repo_type, revision))
if not is_pristine:
output.warn("Repo status is not pristine: there might be modified files")
else:
# Use the proper approach depending on 'revision_mode'
if revision_mode == "hash":
revision = digest.summary_hash
if revisions_enabled:
output.info("Using the exported files summary hash as the recipe"
" revision: {} ".format(revision))
else:
rev_detected, repo_type, is_pristine = _detect_scm_revision(path)
if not rev_detected:
raise ConanException("Cannot detect revision using '{}' mode"
" from repository at '{}'".format(revision_mode, path))

revision = rev_detected

if revisions_enabled:
output.info("Using %s commit as the recipe revision: %s" % (repo_type, revision))
if not is_pristine:
output.warn("Repo status is not pristine: there might be modified files")

with package_layout.update_metadata() as metadata:
metadata.recipe.revision = revision

Expand Down
4 changes: 2 additions & 2 deletions conans/client/conan_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ def inspect(self, path, attributes, remote_name=None):
if not attributes:
attributes = ['name', 'version', 'url', 'homepage', 'license', 'author',
'description', 'topics', 'generators', 'exports', 'exports_sources',
'short_paths', 'apply_env', 'build_policy', 'settings', 'options',
'default_options']
'short_paths', 'apply_env', 'build_policy', 'revision_mode', 'settings',
'options', 'default_options']
for attribute in attributes:
try:
attr = getattr(conanfile, attribute)
Expand Down
1 change: 1 addition & 0 deletions conans/model/conan_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class ConanFile(object):
exports = None
exports_sources = None
generators = ["txt"]
revision_mode = "hash"

# Vars to control the build steps (build(), package())
should_configure = True
Expand Down
44 changes: 44 additions & 0 deletions conans/test/functional/command/export_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import stat
import textwrap
import unittest

from parameterized import parameterized
Expand All @@ -9,6 +10,7 @@
from conans.paths import CONANFILE, CONAN_MANIFEST
from conans.test.utils.cpp_test_files import cpp_hello_conan_files
from conans.test.utils.tools import TestClient
from conans.test.utils.tools import create_local_git_repo
from conans.util.files import load, save


Expand Down Expand Up @@ -419,3 +421,45 @@ def _create_packages_and_builds(self):
save(file_path, content)
file_list.append(file_path)
return file_list


class ExportMetadataTest(unittest.TestCase):
jgsogo marked this conversation as resolved.
Show resolved Hide resolved
conanfile = textwrap.dedent("""
from conans import ConanFile

class Lib(ConanFile):
revision_mode = "{revision_mode}"
""")

summary_hash = {"hash": "bfe8b4a6a2a74966c0c4e0b34705004a",
"auto": "9a5aa68b863d3f6d774b13af32abc6c1"}
jgsogo marked this conversation as resolved.
Show resolved Hide resolved

def test_revision_mode_hash(self):
t = TestClient()
t.save({'conanfile.py': self.conanfile.format(revision_mode="hash")})

ref = ConanFileReference.loads("name/version@user/channel")
t.run("export . {}".format(ref))

meta = t.cache.package_layout(ref, short_paths=False).load_metadata()
self.assertEqual(meta.recipe.revision, self.summary_hash["hash"])

def test_revision_mode_scm(self):
path, rev = create_local_git_repo(
files={'conanfile.py': self.conanfile.format(revision_mode="scm")})
t = TestClient(current_folder=path)

ref = ConanFileReference.loads("name/version@user/channel")
t.run("export . {}".format(ref))

meta = t.cache.package_layout(ref, short_paths=False).load_metadata()
self.assertEqual(meta.recipe.revision, rev)

def test_revision_mode_invalid(self):
conanfile = self.conanfile.format(revision_mode="auto")

t = TestClient()
t.save({'conanfile.py': conanfile})
ref = ConanFileReference.loads("name/version@user/channel")
t.run("export . {}".format(ref), assert_error=True)
self.assertIn("ERROR: Revision mode should be one of 'hash' (default) or 'scm'", t.out)
10 changes: 9 additions & 1 deletion conans/test/functional/command/inspect_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class Pkg(ConanFile):
self.assertIn("version: None", client.out)
client.run("inspect . -a=settings")
self.assertIn("settings: ('os', 'compiler', 'arch')", client.out)
client.run("inspect . -a=revision_mode")
self.assertIn("revision_mode: hash", client.out)

client.run("inspect . -a=unexisting_attr", assert_error=True)
self.assertIn("ERROR: 'Pkg' object has no attribute 'unexisting_attr'", client.out)
Expand Down Expand Up @@ -147,6 +149,7 @@ def build(self):
short_paths: False
apply_env: True
build_policy: None
revision_mode: hash
settings: None
options: None
default_options: None
Expand All @@ -169,6 +172,7 @@ class Pkg(ConanFile):
options = {"foo": [True, False], "bar": [True, False]}
default_options = {"foo": True, "bar": False}
_private = "Nothing"
revision_mode = "scm"
def build(self):
pass
"""
Expand All @@ -188,6 +192,7 @@ def build(self):
short_paths: False
apply_env: True
build_policy: None
revision_mode: scm
settings: ('os', 'arch', 'build_type', 'compiler')
options:
bar: [True, False]
Expand Down Expand Up @@ -229,6 +234,7 @@ class OpenSSLConan(ConanFile):
short_paths: False
apply_env: True
build_policy: None
revision_mode: hash
settings: ('os', 'compiler', 'arch', 'build_type')
options:
386: [True, False]
Expand Down Expand Up @@ -276,6 +282,7 @@ def build(self):
short_paths: False
apply_env: True
build_policy: None
revision_mode: hash
settings: ('os', 'arch', 'build_type', 'compiler')
options:
bar: [True, False]
Expand All @@ -302,11 +309,12 @@ def build(self):
short_paths: False
apply_env: True
build_policy: None
revision_mode: hash
settings: ('os', 'arch', 'build_type', 'compiler')
options:
bar: [True, False]
foo: [True, False]
default_options:
bar: True
foo: True
""", client.out)
""", client.out)
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,64 @@


import unittest
from collections import namedtuple

import six
from mock import mock

from conans.client.cmd.export import _update_revision_in_metadata
from conans.model.ref import ConanFileReference
from conans.paths.package_layouts.package_cache_layout import PackageCacheLayout
from conans.test.utils.test_files import temp_folder
from conans.errors import ConanException
from conans.test.utils.tools import TestBufferConanOutput


class UpdateRevisionInMetadataTests(unittest.TestCase):

def test_warn_not_pristine(self):
output = TestBufferConanOutput()
def setUp(self):
ref = ConanFileReference.loads("lib/version@user/channel")
self.package_layout = PackageCacheLayout(base_folder=temp_folder(), ref=ref,
short_paths=False, no_lock=True)
self.output = TestBufferConanOutput()

def test_scm_warn_not_pristine(self):
with mock.patch("conans.client.cmd.export._detect_scm_revision",
return_value=("revision", "git", False)):
path = digest = None
ref = ConanFileReference.loads("lib/version@user/channel")
package_layout = PackageCacheLayout(base_folder=temp_folder(), ref=ref,
short_paths=False, no_lock=True)
_update_revision_in_metadata(package_layout, True, output, path, digest)
self.assertIn("WARN: Repo status is not pristine: there might be modified files", output)
path = None
digest = namedtuple("Digest", "summary_hash")
_update_revision_in_metadata(self.package_layout, True, self.output,
path, digest, "scm")
self.assertIn("WARN: Repo status is not pristine: there might be modified files",
self.output)

def test_scm_behavior(self):
revision_mode = "scm"

digest = None
path = None
with mock.patch("conans.client.cmd.export._detect_scm_revision",
return_value=("1234", "git", True)):
rev = _update_revision_in_metadata(self.package_layout, True, self.output,
path, digest, revision_mode)
self.assertEqual(rev, "1234")
self.assertIn("Using git commit as the recipe revision", self.output)

def test_hash_behavior(self):
revision_mode = "hash"

digest = namedtuple("Digest", "summary_hash")
digest.summary_hash = "1234"
path = None
rev = _update_revision_in_metadata(self.package_layout, True, self.output,
path, digest, revision_mode)
self.assertEqual(rev, "1234")
self.assertIn("Using the exported files summary hash as the recipe revision", self.output)

def test_invalid_behavior(self):
revision_mode = "auto"
digest = path = None

with six.assertRaisesRegex(self, ConanException, "Revision mode should be"):
_update_revision_in_metadata(self.package_layout, True, self.output,
path, digest, revision_mode)