aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Klaus Aehlig <aehlig@google.com>2018-06-15 06:01:26 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-06-15 06:02:53 -0700
commitb42734f4213e505d9582def4cd25bd510f0909a9 (patch)
treee829f7c821ada256bd5fcab6b8d24132acadd61c
parentf5be981a28fa50ba83391d4cd42a6e8bce07cca0 (diff)
git_repository: return actual commit
Using that repository rules now may return a non-None value, make git_repository return the arguments that make the rule reproducible; in particular, return the actual commit (instead of a tag) and the date of the commit, so support shallow clones. The added test also demonstrates how the `bazel sync` command together with `--experimental_repository_resolved_file` can be used to replay an earlier state of external dependencies. Change-Id: Ifa1cfdfdb5eb299a15b9d0ec7d285dc84c0bcdc0 PiperOrigin-RevId: 200705705
-rw-r--r--src/test/shell/bazel/BUILD2
-rwxr-xr-xsrc/test/shell/bazel/workspace_resolved_test.sh77
-rw-r--r--tools/build_defs/repo/git.bzl81
3 files changed, 140 insertions, 20 deletions
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 045f33ed19..0b38915e46 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -369,7 +369,7 @@ sh_test(
size = "medium",
srcs = ["workspace_resolved_test.sh"],
data = [":test-deps"],
- shard_count = 3,
+ shard_count = 4,
)
sh_test(
diff --git a/src/test/shell/bazel/workspace_resolved_test.sh b/src/test/shell/bazel/workspace_resolved_test.sh
index 8504bb1512..d00202275c 100755
--- a/src/test/shell/bazel/workspace_resolved_test.sh
+++ b/src/test/shell/bazel/workspace_resolved_test.sh
@@ -90,6 +90,83 @@ EOF
|| fail "Not the correct number of original attributes"
}
+test_git_return_value() {
+ EXTREPODIR=`pwd`
+ export GIT_CONFIG_NOSYSTEM=YES
+
+ mkdir extgit
+ (cd extgit && git init \
+ && git config user.email 'me@example.com' \
+ && git config user.name 'E X Ample' )
+ echo Hello World > extgit/hello.txt
+ (cd extgit
+ git add .
+ git commit --author="A U Thor <author@example.com>" -m 'initial commit'
+ git tag mytag)
+
+ # Check out the external git repository at the given tag, and record
+ # the return value of the git rule.
+ mkdir tagcheckout
+ cd tagcheckout
+ cat > WORKSPACE <<EOF
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
+new_git_repository(
+ name="ext",
+ remote="file://${EXTREPODIR}/extgit/.git",
+ tag="mytag",
+ build_file_content="exports_files([\"hello.txt\"])",
+)
+EOF
+ bazel sync --experimental_repository_resolved_file=../repo.bzl
+ # some of the file systems on our test machines are really slow to
+ # notice the creation of a file---even after the call to sync(1).
+ bazel shutdown; sync; sleep 10
+
+ cd ..
+ echo; cat repo.bzl; echo
+
+ # Now add an additional commit to the upstream repository and
+ # force update the tag
+ echo CHANGED > extgit/hello.txt
+ (cd extgit
+ git add .
+ git commit --author="A U Thor <author@example.com>" -m 'change hello.txt'
+ git tag -f mytag)
+
+ # Verify that the recorded resolved information is what we expect. In
+ # particular, verify that we don't get the new upstream commit.
+ mkdir analysisrepo
+ cd analysisrepo
+ cp ../repo.bzl .
+ cat > workspace.bzl <<'EOF'
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
+load("//:repo.bzl", "resolved")
+
+def repo():
+ for entry in resolved:
+ if entry["original_attributes"]["name"] == "ext":
+ new_git_repository(**(entry["repositories"][0]["attributes"]))
+EOF
+ cat > WORKSPACE <<'EOF'
+load("//:workspace.bzl", "repo")
+repo()
+EOF
+ cat > BUILD <<'EOF'
+genrule(
+ name = "out",
+ outs = ["out.txt"],
+ srcs = ["@ext//:hello.txt"],
+ cmd = "cp $< $@",
+)
+EOF
+ bazel build //:out
+ grep "Hello World" `bazel info bazel-genfiles`/out.txt \
+ || fail "ext not taken at the right commit"
+ grep "CHANGED" `bazel info bazel-genfiles`/out.txt \
+ && fail "not taking the frozen commit" || :
+}
+
+
test_sync_calls_all() {
mkdir sync_calls_all && cd sync_calls_all
rm -rf fetchrepo
diff --git a/tools/build_defs/repo/git.bzl b/tools/build_defs/repo/git.bzl
index a46330542c..3663dc804f 100644
--- a/tools/build_defs/repo/git.bzl
+++ b/tools/build_defs/repo/git.bzl
@@ -80,17 +80,37 @@ set -ex
if st.return_code:
fail("error updating submodules %s:\n%s" % (ctx.name, st.stderr))
-def _new_git_repository_implementation(ctx):
- if ((not ctx.attr.build_file and not ctx.attr.build_file_content) or
- (ctx.attr.build_file and ctx.attr.build_file_content)):
- fail("Exactly one of build_file and build_file_content must be provided.")
- _clone_or_update(ctx)
- workspace_and_buildfile(ctx)
- patch(ctx)
-
-def _git_repository_implementation(ctx):
- _clone_or_update(ctx)
- patch(ctx)
+ # After the fact, determine the actual commit and its date
+ actual_commit = ctx.execute([
+ bash_exe,
+ "-c",
+ "(cd '{directory}' && git log -n 1 --pretty='format:%H')".format(
+ directory = ctx.path("."),
+ ),
+ ]).stdout
+ shallow_date = ctx.execute([
+ bash_exe,
+ "-c",
+ "(cd '{directory}' && git log -n 1 --pretty='format:%cd' --date='format:%Y-%d-%m')".format(
+ directory = ctx.path("."),
+ ),
+ ]).stdout
+ return {"commit": actual_commit, "shallow_since": shallow_date}
+
+def _update_commit(orig, keys, override):
+ # Merge the override information into the dict, resulting by taking the
+ # given keys, as well as the name, form orig (if present there).
+ result = {}
+ for key in keys:
+ if getattr(orig, key) != None:
+ result[key] = getattr(orig, key)
+ result["name"] = orig.name
+ result.update(override)
+
+ # remove tag if we found the actual commit
+ if "commit" in result:
+ result.pop("tag", None)
+ return result
_common_attrs = {
"remote": attr.string(mandatory = True),
@@ -106,19 +126,38 @@ _common_attrs = {
"patch_cmds": attr.string_list(default = []),
}
+_new_git_repository_attrs = dict(_common_attrs.items() + {
+ "build_file": attr.label(allow_single_file = True),
+ "build_file_content": attr.string(),
+ "workspace_file": attr.label(),
+ "workspace_file_content": attr.string(),
+}.items())
+
+def _new_git_repository_implementation(ctx):
+ if ((not ctx.attr.build_file and not ctx.attr.build_file_content) or
+ (ctx.attr.build_file and ctx.attr.build_file_content)):
+ fail("Exactly one of build_file and build_file_content must be provided.")
+ update = _clone_or_update(ctx)
+ workspace_and_buildfile(ctx)
+ patch(ctx)
+ return _update_commit(ctx.attr, _new_git_repository_attrs.keys(), update)
+
+def _git_repository_implementation(ctx):
+ update = _clone_or_update(ctx)
+ patch(ctx)
+ return _update_commit(ctx.attr, _common_attrs.keys(), update)
+
new_git_repository = repository_rule(
implementation = _new_git_repository_implementation,
- attrs = dict(_common_attrs.items() + {
- "build_file": attr.label(allow_single_file = True),
- "build_file_content": attr.string(),
- "workspace_file": attr.label(),
- "workspace_file_content": attr.string(),
- }.items()),
+ attrs = _new_git_repository_attrs,
)
"""Clone an external git repository.
Clones a Git repository, checks out the specified tag, or commit, and
-makes its targets available for binding.
+makes its targets available for binding. Also determine the id of the
+commit actually checkted out and its date, and return a dict with paramters
+that provide a reproducible version of this rule (which a tag not necessarily
+is).
Args:
name: A unique name for this rule.
@@ -173,7 +212,11 @@ git_repository = repository_rule(
"""Clone an external git repository.
Clones a Git repository, checks out the specified tag, or commit, and
-makes its targets available for binding.
+makes its targets available for binding. Also determine the id of the
+commit actually checkted out and its date, and return a dict with paramters
+that provide a reproducible version of this rule (which a tag not necessarily
+is).
+
Args:
name: A unique name for this rule.