Skip to content

Commit

Permalink
Allow to specify revision_mode for each recipe (#4767)
Browse files Browse the repository at this point in the history
* show warning if repo is not pristine when using SCM for revision

* return 3, not 2 values

* add 'revision_mode' attribute to conanfile

* add unittests for revision_mode

* add test for attribute inspection

* run tests using a conanfile

* different approach for scm with repo

* default for revision_mode is 'hash', value 'auto' is no longer needed

* auto is no longer a valid revision_mode
  • Loading branch information
jgsogo authored and lasote committed Mar 25, 2019
1 parent 0bce0fb commit b9a7696
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 24 deletions.
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
43 changes: 43 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,44 @@ def _create_packages_and_builds(self):
save(file_path, content)
file_list.append(file_path)
return file_list


class ExportMetadataTest(unittest.TestCase):
conanfile = textwrap.dedent("""
from conans import ConanFile
class Lib(ConanFile):
revision_mode = "{revision_mode}"
""")

summary_hash = "bfe8b4a6a2a74966c0c4e0b34705004a"

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)

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)

0 comments on commit b9a7696

Please sign in to comment.