dfee7b6196
GitOrigin-RevId: b578e69f18a543889ded9c57a8f0dffacdb103d8
1242 lines
36 KiB
Bash
Executable file
1242 lines
36 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Copyright 2016 Google Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
source "${TEST_SRCDIR}/copybara/third_party/bazel/bashunit/unittest.bash"
|
|
|
|
# This should be kept in sync with ExitCode
|
|
readonly SUCCESS=0
|
|
readonly COMMAND_LINE_ERROR=1
|
|
readonly CONFIGURATION_ERROR=2
|
|
readonly REPOSITORY_ERROR=3
|
|
readonly NO_OP=4
|
|
readonly INTERRUPTED=8
|
|
readonly ENVIRONMENT_ERROR=30
|
|
readonly INTERNAL_ERROR=31
|
|
|
|
function run_git() {
|
|
git "$@" > $TEST_log 2>&1 || fail "Error running git"
|
|
}
|
|
|
|
# A log configuration that outputs to the console, so that we can check the log easier
|
|
log_config=$(mktemp)
|
|
cat > $log_config <<'EOF'
|
|
handlers=java.util.logging.ConsoleHandler
|
|
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n
|
|
EOF
|
|
|
|
# Extracted as a variable so that internal tests can extend the test with different binary
|
|
copybara_binary="${copybara_binary-"${TEST_SRCDIR}/copybara/java/com/google/copybara/copybara"}"
|
|
|
|
function copybara() {
|
|
$copybara_binary --jvm_flag=-Djava.util.logging.config.file=$log_config "$@" \
|
|
--output-root "$repo_storage" \
|
|
--work-dir "$workdir" > $TEST_log 2>&1 \
|
|
&& return
|
|
|
|
res=$?
|
|
printf 'Copybara process returned error %d:\n' $res
|
|
cat $TEST_log
|
|
return $res
|
|
}
|
|
|
|
function set_up() {
|
|
# Avoid reusing the same directory for each tests so that we don't
|
|
# share state between tests.
|
|
cd "$(mktemp -d)" || return
|
|
# set XDG_CACHE_HOME so that we have a writeable place for our caches
|
|
export XDG_CACHE_HOME="$(mktemp -d)"
|
|
# An early check to avoid confusing test failures
|
|
git version || fail "Git doesn't seem to be installed. Cannot test without git command."
|
|
|
|
export HOME="$(mktemp -d)"
|
|
export repo_storage=$(temp_dir storage)
|
|
export workdir=$(temp_dir workdir)
|
|
git config --global user.name 'Bara Kopi'
|
|
git config --global user.email 'bara@kopi.com'
|
|
}
|
|
|
|
function temp_dir() {
|
|
echo "$PWD/$(mktemp -d $1.XXXXXXXXXX)"
|
|
}
|
|
|
|
function expect_in_file() {
|
|
local regex="$1"
|
|
local file="$2"
|
|
cat "$file" > $TEST_log || fail "'$file' not found"
|
|
expect_log "$regex" || fail "Cannot find '$regex' in '$file'"
|
|
}
|
|
|
|
# Runs Copybara and check that the exit code is the expected one.
|
|
function copybara_with_exit_code() {
|
|
local expected_code=$1
|
|
shift
|
|
copybara "$@" && status=$? || status=$?
|
|
if [ $status -ne $expected_code ]; then
|
|
fail "Unexpected exit code $status. Expected $expected_code."
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function check_copybara_rev_id() {
|
|
local repo="$1"
|
|
local origin_id="$2"
|
|
( cd $repo || return
|
|
run_git log master -1 > $TEST_log
|
|
expect_log "GitOrigin-RevId: $origin_id"
|
|
)
|
|
}
|
|
|
|
function test_git_tracking() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
echo "first version for food and foooooo" > test.txt
|
|
mkdir subdir
|
|
echo "first version for food and fools" > subdir/test.txt
|
|
run_git add test.txt subdir/test.txt
|
|
run_git commit -m "first commit"
|
|
first_commit=$(run_git rev-parse HEAD)
|
|
popd || return
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
core.replace(
|
|
before = "food",
|
|
after = "drink"
|
|
),
|
|
core.replace(
|
|
before = "f\${os}o",
|
|
after = "bar\${os}",
|
|
regex_groups = {
|
|
"os" : "o+"
|
|
},
|
|
)
|
|
],
|
|
)
|
|
EOF
|
|
|
|
copybara copy.bara.sky --force
|
|
|
|
expect_log '\[ 1/2\] Transform Replace food'
|
|
expect_log '\[ 2/2\] Transform Replace f\${os}o'
|
|
|
|
# Make sure we don't get detached head warnings polluting the log.
|
|
expect_not_log "You are in 'detached HEAD' state"
|
|
|
|
[[ -f $workdir/checkout/test.txt ]] || fail "Checkout was not successful"
|
|
cat $workdir/checkout/test.txt > $TEST_log
|
|
expect_log "first version for drink and barooooo"
|
|
cat $workdir/checkout/subdir/test.txt > $TEST_log
|
|
expect_in_file "first version for drink and barols" $workdir/checkout/subdir/test.txt
|
|
check_copybara_rev_id "$destination" "$first_commit"
|
|
|
|
# Do a new modification and check that we are tracking the changes to the branch
|
|
pushd $remote || return
|
|
echo "second version for food and foooooo" > test.txt
|
|
echo "second version for food and fools" > subdir/test.txt
|
|
|
|
run_git add test.txt
|
|
run_git commit -m "second commit"
|
|
second_commit=$(git rev-parse HEAD)
|
|
popd || return
|
|
|
|
copybara copy.bara.sky
|
|
|
|
[[ -f $workdir/checkout/test.txt ]] || fail "Checkout was not successful"
|
|
expect_in_file "second version for drink and barooooo" $workdir/checkout/test.txt
|
|
|
|
check_copybara_rev_id "$destination" "$second_commit"
|
|
|
|
( cd $destination || return
|
|
run_git show master > $TEST_log
|
|
)
|
|
|
|
expect_log "-first version for drink"
|
|
expect_log "+second version for drink and barooooo"
|
|
}
|
|
|
|
function test_git_iterative() {
|
|
remote=$(temp_dir remote)
|
|
repo_storage=$(temp_dir storage)
|
|
workdir=$(temp_dir workdir)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
commit_one=$(single_file_commit "commit one" file.txt "food fooooo content1")
|
|
commit_two=$(single_file_commit "commit two" file.txt "food fooooo content2")
|
|
commit_three=$(single_file_commit "commit three" file.txt "food fooooo content3")
|
|
commit_four=$(single_file_commit "commit four" file.txt "food fooooo content4")
|
|
commit_five=$(single_file_commit "commit five" file.txt "food fooooo content5")
|
|
|
|
popd || return
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
mode = "ITERATIVE",
|
|
)
|
|
EOF
|
|
|
|
copybara copy.bara.sky default $commit_three --last-rev $commit_one
|
|
|
|
check_copybara_rev_id "$destination" "$commit_three"
|
|
|
|
( cd $destination || return
|
|
run_git log master~1..master > $TEST_log
|
|
)
|
|
expect_not_log "commit two"
|
|
expect_log "commit three"
|
|
|
|
copybara copy.bara.sky default $commit_five
|
|
|
|
check_copybara_rev_id "$destination" "$commit_five"
|
|
|
|
( cd $destination || return
|
|
run_git log master~2..master~1 > $TEST_log
|
|
)
|
|
expect_log "commit four"
|
|
|
|
( cd $destination || return
|
|
run_git log master~1..master > $TEST_log
|
|
)
|
|
expect_log "commit five"
|
|
|
|
# Running the same workflow with the same ref is no-op
|
|
copybara_with_exit_code $NO_OP copy.bara.sky default $commit_three
|
|
expect_log "No new changes to import for resolved ref"
|
|
}
|
|
|
|
function single_file_commit() {
|
|
message=$1
|
|
file=$2
|
|
content=$3
|
|
echo $content > $file
|
|
run_git add $file
|
|
run_git commit -m "$message"
|
|
git rev-parse HEAD
|
|
}
|
|
|
|
function test_get_git_changes() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
commit_one=$(single_file_commit "commit one" file.txt "food fooooo content1")
|
|
commit_two=$(single_file_commit "commit two" file.txt "food fooooo content2")
|
|
commit_three=$(single_file_commit "commit three" file.txt "food fooooo content3")
|
|
commit_four=$(single_file_commit "commit four" file.txt "food fooooo content4")
|
|
commit_five=$(single_file_commit "commit five" file.txt "food fooooo content5")
|
|
popd || return
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
metadata.squash_notes()
|
|
],
|
|
)
|
|
EOF
|
|
|
|
# Before running the tool for the first time, the last imported ref is empty
|
|
copybara info copy.bara.sky default
|
|
expect_log "'workflow_default': last_migrated None - last_available $commit_five"
|
|
expect_log "Available changes (5):"
|
|
expect_log ".*${commit_one:0:10}.*commit one.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_two:0:10}.*commit two.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_three:0:10}.*commit three.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_four:0:10}.*commit four.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_five:0:10}.*commit five.*Bara Kopi <bara@kopi.com>.*"
|
|
|
|
copybara copy.bara.sky default $commit_one --force
|
|
|
|
check_copybara_rev_id "$destination" "$commit_one"
|
|
|
|
copybara info copy.bara.sky default
|
|
expect_log "'workflow_default': last_migrated $commit_one - last_available $commit_five"
|
|
expect_log "Available changes (4):"
|
|
expect_log ".*${commit_two:0:10}.*commit two.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_three:0:10}.*commit three.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_four:0:10}.*commit four.*Bara Kopi <bara@kopi.com>.*"
|
|
expect_log ".*${commit_five:0:10}.*commit five.*Bara Kopi <bara@kopi.com>.*"
|
|
|
|
( cd $destination || return
|
|
run_git log master~1..master > $TEST_log
|
|
)
|
|
# By default we include the whole history if last_rev cannot be found. --squash-without-history
|
|
# can be used for disabling this.
|
|
expect_log "commit one"
|
|
|
|
copybara copy.bara.sky default $commit_four
|
|
|
|
copybara info copy.bara.sky default
|
|
expect_log "'workflow_default': last_migrated $commit_four - last_available $commit_five"
|
|
expect_log "Available changes (1):"
|
|
expect_log ".*${commit_five:0:10}.*commit five.*Bara Kopi <bara@kopi.com>.*"
|
|
|
|
check_copybara_rev_id "$destination" "$commit_four"
|
|
|
|
( cd $destination || return
|
|
run_git log master~1..master > $TEST_log
|
|
)
|
|
# "commit one" should not be included because it was migrated before
|
|
expect_not_log "commit one"
|
|
expect_log "$commit_two.*commit two"
|
|
expect_log "$commit_three.*commit three"
|
|
expect_log "$commit_four.*commit four"
|
|
expect_not_log "commit five"
|
|
|
|
copybara copy.bara.sky default $commit_five --last-rev $commit_three
|
|
|
|
check_copybara_rev_id "$destination" "$commit_five"
|
|
|
|
( cd $destination || return
|
|
run_git log master~1..master > $TEST_log
|
|
)
|
|
# We are forcing to use commit_three as the last migration. This has
|
|
# no effect in squash workflow but it changes the release notes.
|
|
expect_not_log "commit one"
|
|
expect_not_log "commit two"
|
|
expect_not_log "commit three"
|
|
expect_log "$commit_four.*commit four"
|
|
expect_log "$commit_five.*commit five"
|
|
}
|
|
|
|
function test_can_skip_excluded_commit() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
commit_master=$(single_file_commit "last rev commit" file2.txt "origin")
|
|
commit_one=$(single_file_commit "commit one" file.txt "foooo")
|
|
# Because we exclude file2.txt this is effectively an empty commit in the destination
|
|
commit_two=$(single_file_commit "commit two" file2.txt "bar")
|
|
commit_three=$(single_file_commit "commit three" file.txt "baaaz")
|
|
popd || return
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
origin_files = glob(include = ["**"], exclude = ["file2.txt"]),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
mode = "ITERATIVE",
|
|
)
|
|
EOF
|
|
|
|
copybara copy.bara.sky default $commit_three --last-rev $commit_master --force
|
|
|
|
check_copybara_rev_id "$destination" "$commit_three"
|
|
|
|
( cd $destination || return
|
|
run_git log > $TEST_log
|
|
)
|
|
expect_log "commit one"
|
|
expect_not_log "commit two"
|
|
expect_log "commit three"
|
|
}
|
|
|
|
function empty_git_bare_repo() {
|
|
repo=$(temp_dir repo)
|
|
cd $repo || return
|
|
run_git init . --bare > $TEST_log 2>&1 || fail "Cannot create repo"
|
|
run_git --work-tree="$(mktemp -d)" commit --allow-empty -m "Empty repo" \
|
|
> $TEST_log 2>&1 || fail "Cannot commit to empty repo"
|
|
echo $repo
|
|
}
|
|
|
|
function prepare_glob_tree() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "foo" > test.txt
|
|
echo "foo" > test.java
|
|
mkdir -p folder/subfolder
|
|
echo "foo" > folder/test.txt
|
|
echo "foo" > folder/test.java
|
|
echo "foo" > folder/subfolder/test.txt
|
|
echo "foo" > folder/subfolder/test.java
|
|
run_git add -A
|
|
run_git commit -m "first commit"
|
|
)
|
|
}
|
|
|
|
function test_regex_with_path() {
|
|
prepare_glob_tree
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
core.replace(
|
|
before = "foo",
|
|
after = "bar",
|
|
paths = glob(['**.java']),
|
|
)
|
|
],
|
|
)
|
|
EOF
|
|
copybara copy.bara.sky --force
|
|
( cd "$(mktemp -d)" || return
|
|
run_git clone $destination .
|
|
expect_in_file "foo" test.txt
|
|
expect_in_file "bar" test.java
|
|
expect_in_file "foo" folder/test.txt
|
|
expect_in_file "bar" folder/test.java
|
|
expect_in_file "foo" folder/subfolder/test.txt
|
|
expect_in_file "bar" folder/subfolder/test.java
|
|
)
|
|
}
|
|
|
|
function git_pull_request() {
|
|
sot=$(empty_git_bare_repo)
|
|
public=$(empty_git_bare_repo)
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "export",
|
|
origin = git.origin(
|
|
url = "file://$sot",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$public",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
)
|
|
|
|
core.workflow(
|
|
name = "import_change",
|
|
origin = git.origin(
|
|
url = "file://$public",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$sot",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
mode = "CHANGE_REQUEST",
|
|
)
|
|
EOF
|
|
|
|
# Create the SoT repo
|
|
pushd "$(mktemp -d)" || return
|
|
run_git clone $sot .
|
|
commit_one=$(single_file_commit "commit one" one.txt "content")
|
|
commit_two=$(single_file_commit "commit two" two.txt "content")
|
|
commit_three=$(single_file_commit "commit three" three.txt "content")
|
|
run_git push
|
|
popd || return
|
|
|
|
copybara copy.bara.sky export $commit_two
|
|
|
|
# Check that we have exported correctly the tree state as in commit_two
|
|
pushd "$(mktemp -d)" || return
|
|
run_git clone $public .
|
|
[[ -f one.txt ]] || fail "one.txt should exist in commit two"
|
|
[[ -f two.txt ]] || fail "two.txt should exist in commit two"
|
|
[[ ! -f three.txt ]] || fail "three.txt should NOT exist in commit two"
|
|
|
|
# Create a new change on top of the public version (commit_two)
|
|
pr_request=$(single_file_commit "pull request" pr.txt "content")
|
|
run_git push
|
|
popd || return
|
|
|
|
copybara copy.bara.sky import_change $pr_request
|
|
|
|
# Check that the SoT contains the change from pr_request but that it has not reverted
|
|
# commit_three (SoT was ahead of public).
|
|
pushd "$(mktemp -d)" || return
|
|
run_git clone $sot .
|
|
[[ -f one.txt ]] || fail "one.txt should exist after pr import"
|
|
[[ -f two.txt ]] || fail "two.txt should exist after pr import"
|
|
[[ -f three.txt ]] || fail "three.txt should exist after pr import"
|
|
[[ -f pr.txt ]] || fail "pr.txt should exist after pr import"
|
|
popd || return
|
|
}
|
|
|
|
function test_git_delete() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "first version for food and foooooo" > test.txt
|
|
mkdir subdir
|
|
echo "first version" > subdir/test.txt
|
|
mkdir subdir2
|
|
echo "first version" > subdir2/test.java
|
|
echo "first version" > subdir2/test.txt
|
|
run_git add -A
|
|
run_git commit -m "first commit"
|
|
)
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
origin_files = glob(include = ["**"], exclude = ['**/*.java', 'subdir/**']),
|
|
)
|
|
EOF
|
|
copybara copy.bara.sky --force
|
|
|
|
( cd "$(mktemp -d)" || return
|
|
run_git clone $destination .
|
|
[[ ! -f subdir/test.txt ]] || fail "/subdir/test.txt should be deleted"
|
|
[[ ! -f subdir2/test.java ]] || fail "/subdir2/test.java should be deleted"
|
|
|
|
[[ -f test.txt ]] || fail "/test.txt should not be deleted"
|
|
[[ ! -d subdir ]] || fail "/subdir should be deleted"
|
|
[[ -d subdir2 ]] || fail "/subdir2 should not be deleted"
|
|
[[ -f subdir2/test.txt ]] || fail "/subdir2/test.txt should not be deleted"
|
|
)
|
|
}
|
|
|
|
function test_reverse_sequence() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "foobaz" > test.txt
|
|
run_git add -A
|
|
run_git commit -m "first commit"
|
|
)
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
forward_transforms = [
|
|
core.replace('foo', 'bar'),
|
|
core.replace('baz', 'bee'),
|
|
]
|
|
core.workflow(
|
|
name = "forward",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = forward_transforms,
|
|
)
|
|
|
|
core.workflow(
|
|
name = "reverse",
|
|
origin = git.origin(
|
|
url = "file://$destination",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$remote",
|
|
fetch = "reverse",
|
|
push = "reverse",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = core.reverse(forward_transforms),
|
|
)
|
|
EOF
|
|
copybara copy.bara.sky forward --force
|
|
|
|
( cd "$(mktemp -d)" || return
|
|
run_git clone $destination .
|
|
[[ -f test.txt ]] || fail "/test.txt should exit"
|
|
expect_in_file "barbee" test.txt
|
|
)
|
|
copybara copy.bara.sky reverse --force
|
|
|
|
( cd "$(mktemp -d)" || return
|
|
run_git clone $remote .
|
|
run_git checkout reverse
|
|
[[ -f test.txt ]] || fail "/test.txt should exit"
|
|
expect_in_file "foobaz" test.txt
|
|
)
|
|
}
|
|
|
|
function test_local_dir_destination() {
|
|
remote=$(temp_dir remote)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "first version for food and foooooo" > test.txt
|
|
echo "first version" > test.txt
|
|
run_git add test.txt
|
|
run_git commit -m "first commit"
|
|
)
|
|
mkdir destination
|
|
|
|
cat > destination/copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination_files = glob(include = ["**"], exclude = ["copy.bara.sky", "**.keep"]),
|
|
destination = folder.destination(),
|
|
reversible_check = True, # enabled to test for regression where folder.origin caused exceptions
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
)
|
|
EOF
|
|
|
|
touch destination/keepme.keep
|
|
mkdir -p destination/folder
|
|
touch destination/folder/keepme.keep
|
|
touch destination/dontkeep.txt
|
|
|
|
copybara destination/copy.bara.sky --folder-dir destination
|
|
|
|
[[ -f destination/test.txt ]] || fail "test.txt should exist"
|
|
[[ -f destination/copy.bara.sky ]] || fail "copy.bara.sky should exist"
|
|
[[ -f destination/keepme.keep ]] || fail "keepme.keep should exist"
|
|
[[ -f destination/folder/keepme.keep ]] || fail "folder/keepme.keep should exist"
|
|
[[ ! -f destination/dontkeep.txt ]] || fail "dontkeep.txt should be deleted"
|
|
}
|
|
|
|
function test_choose_non_default_workflow() {
|
|
remote=$(temp_dir remote)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "foo" > test.txt
|
|
run_git add test.txt
|
|
run_git commit -m "first commit"
|
|
)
|
|
mkdir destination
|
|
|
|
cat > destination/copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = folder.destination(),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
)
|
|
|
|
core.workflow(
|
|
name = "choochoochoose_me",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = folder.destination(),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [core.replace("foo", "bar")]
|
|
)
|
|
EOF
|
|
|
|
copybara destination/copy.bara.sky choochoochoose_me --folder-dir destination
|
|
expect_in_file "bar" destination/test.txt
|
|
}
|
|
|
|
function test_file_move() {
|
|
remote=$(temp_dir remote)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "foo" > test1.txt
|
|
echo "foo" > test2.txt
|
|
run_git add test1.txt test2.txt
|
|
run_git commit -m "first commit"
|
|
)
|
|
mkdir destination
|
|
|
|
cat > destination/copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = folder.destination(),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
core.move('test1.txt', 'test1.moved'),
|
|
core.move('test2.txt', 'test2.moved'),
|
|
],
|
|
)
|
|
EOF
|
|
|
|
copybara destination/copy.bara.sky --folder-dir destination
|
|
|
|
expect_in_file "foo" destination/test1.moved
|
|
expect_in_file "foo" destination/test2.moved
|
|
[[ ! -f destination/test1.txt ]] || fail "test1.txt should have been moved"
|
|
[[ ! -f destination/test2.txt ]] || fail "test2.txt should have been moved"
|
|
}
|
|
|
|
function test_profile() {
|
|
remote=$(temp_dir remote)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "foo" > test.txt
|
|
run_git add test.txt
|
|
run_git commit -m "first commit"
|
|
)
|
|
mkdir destination
|
|
|
|
cat > destination/copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = folder.destination(),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
core.move('test.txt', 'test.moved'),
|
|
core.move('test.moved', 'test.moved2'),
|
|
],
|
|
)
|
|
EOF
|
|
|
|
copybara destination/copy.bara.sky --folder-dir destination
|
|
expect_log "ioRepoTask.*PROFILE:.*[0-9]* //copybara/clean_outputdir"
|
|
expect_log "repoTask.*PROFILE:.*[0-9]* //copybara/run/default/origin.resolve_source_ref"
|
|
expect_log "doMigrate.*PROFILE:.*[0-9]* //copybara/run/default/squash/prepare_workdir"
|
|
expect_log "checkout.*PROFILE:.*[0-9]* //copybara/run/default/squash/origin.checkout"
|
|
expect_log "transform.*PROFILE:.*[0-9]* //copybara/run/default/squash/transforms/Moving test.txt"
|
|
expect_log "transform.*PROFILE:.*[0-9]* //copybara/run/default/squash/transforms/Moving test.moved"
|
|
expect_log "doMigrate.*PROFILE:.*[0-9]* //copybara/run/default/squash/transforms"
|
|
expect_log "doMigrate.*PROFILE:.*[0-9]* //copybara/run/default/squash/destination.write"
|
|
expect_log "run.*PROFILE:.*[0-9]* //copybara/run/default/squash"
|
|
expect_log "run.*PROFILE:.*[0-9]* //copybara/run"
|
|
expect_log "shutdown.*PROFILE:.*[0-9]* //copybara"
|
|
expect_in_file "foo" destination/test.moved2
|
|
}
|
|
|
|
function test_invalid_transformations_in_config() {
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://foo/bar",
|
|
ref = "master",
|
|
),
|
|
destination = folder.destination(),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [42],
|
|
)
|
|
EOF
|
|
copybara_with_exit_code $CONFIGURATION_ERROR copy.bara.sky default
|
|
expect_log "for 'transformations' element, got int, want function or transformation"
|
|
}
|
|
|
|
function test_command_help_flag() {
|
|
copybara help
|
|
expect_log 'Usage: copybara \[options\]'
|
|
expect_log 'Example:'
|
|
}
|
|
|
|
# We want to log the command line arguments so that it is easy to reproduce
|
|
# user issues.
|
|
function test_command_args_logged() {
|
|
copybara foo bar baz --option && fail "should fail"
|
|
expect_log 'Running: .*foo bar baz --option'
|
|
}
|
|
|
|
function test_command_copybara_filename_no_correct_name() {
|
|
copybara_with_exit_code $CONFIGURATION_ERROR migrate somename.bzl
|
|
expect_log "Copybara config file filename should be 'copy.bara.sky'"
|
|
}
|
|
|
|
function setup_reversible_check_workflow() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
echo "Is the reverse of the reverse forward?" > test.txt
|
|
run_git add test.txt
|
|
run_git commit -m "first commit"
|
|
first_commit=$(run_git rev-parse HEAD)
|
|
popd || return
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
reversible_check = True,
|
|
transformations = [
|
|
core.replace(
|
|
before = "reverse",
|
|
after = "forward"
|
|
)
|
|
],
|
|
)
|
|
EOF
|
|
}
|
|
|
|
function test_reversible_check() {
|
|
setup_reversible_check_workflow
|
|
copybara_with_exit_code $CONFIGURATION_ERROR copy.bara.sky --force
|
|
expect_log "ERROR: Workflow 'default' is not reversible"
|
|
}
|
|
|
|
function test_disable_reversible_check() {
|
|
setup_reversible_check_workflow
|
|
copybara --disable-reversible-check copy.bara.sky --force
|
|
}
|
|
|
|
function test_config_not_found() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR copy.bara.sky origin/master
|
|
expect_log "Configuration file not found: copy.bara.sky"
|
|
}
|
|
|
|
#Verify that we instantiate LogConsole when System.console() is null
|
|
function test_no_ansi_console() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR copy.bara.sky
|
|
expect_log "^[0-9]\{4\} [0-2][0-9]:[0-5][0-9]:[0-5][0-9].*"
|
|
}
|
|
|
|
# Verify that Copybara fails if we try to read the input from the user from a writeOnly LogConsole
|
|
function test_log_console_is_write_only() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
( cd $remote || return
|
|
run_git init .
|
|
echo "first version for food and foooooo" > test.txt
|
|
run_git add -A
|
|
run_git commit -m "first commit"
|
|
)
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
ask_for_confirmation = True,
|
|
)
|
|
EOF
|
|
copybara_with_exit_code $INTERNAL_ERROR copy.bara.sky --force
|
|
expect_log "LogConsole cannot read user input if system console is not present"
|
|
}
|
|
|
|
function run_multifile() {
|
|
config_folder=$1
|
|
shift
|
|
flags="$*"
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
echo "first version for food and foooooo" > test.txt
|
|
mkdir subdir
|
|
echo "first version" > subdir/test.txt
|
|
run_git add test.txt subdir/test.txt
|
|
run_git commit -m "first commit"
|
|
first_commit=$(run_git rev-parse HEAD)
|
|
popd || return
|
|
mkdir -p $config_folder/foo/bar/baz
|
|
mkdir -p $config_folder/baz
|
|
# Just in case:
|
|
cat > $config_folder/baz/origin.bara.sky <<EOF
|
|
this shouldn't be loaded!
|
|
EOF
|
|
cat > $config_folder/foo/remote.bara.sky <<EOF
|
|
remote_var="file://$remote"
|
|
EOF
|
|
cat > $config_folder/foo/bar/baz/origin.bara.sky <<EOF
|
|
load("//foo/remote", "remote_var")
|
|
remote_origin=git.origin( url = remote_var, ref = "master",)
|
|
EOF
|
|
cat > $config_folder/foo/bar/copy.bara.sky <<EOF
|
|
load("baz/origin", "remote_origin")
|
|
|
|
core.workflow(
|
|
name = "default",
|
|
origin = remote_origin,
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
)
|
|
EOF
|
|
cd $config_folder || return
|
|
copybara foo/bar/copy.bara.sky $flags --force
|
|
|
|
[[ -f $workdir/checkout/test.txt ]] || fail "Checkout was not successful"
|
|
}
|
|
|
|
# Test that we can find the root when config is in a git repo
|
|
function test_multifile_git_root() {
|
|
config_folder=$(temp_dir config)
|
|
pushd $config_folder || return
|
|
run_git init .
|
|
popd || return
|
|
run_multifile $config_folder
|
|
}
|
|
|
|
# Test that on non-git repos we can pass a flag to set the root
|
|
function test_multifile_root_cfg_flag() {
|
|
config_folder=$(temp_dir config)
|
|
run_multifile $config_folder --config-root $config_folder
|
|
}
|
|
|
|
# Regression test: config roots that are symlinks work
|
|
function test_multifile_root_cfg_flag_symlink() {
|
|
config_folder=$(temp_dir config)
|
|
mkdir "${config_folder}/test"
|
|
ln -s "${config_folder}/test" "${config_folder}/link"
|
|
run_multifile "${config_folder}/link" --config-root "${config_folder}/link"
|
|
}
|
|
|
|
function test_verify_match() {
|
|
prepare_glob_tree
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
core.verify_match(
|
|
regex = "bar",
|
|
paths = glob(['**.java']),
|
|
verify_no_match = True
|
|
)
|
|
],
|
|
)
|
|
EOF
|
|
copybara copy.bara.sky --force
|
|
}
|
|
|
|
function test_subcommand_parsing_fails() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR migrate.sky copy.bara.sky
|
|
|
|
expect_log "Invalid subcommand 'migrate.sky'"
|
|
}
|
|
|
|
function test_command_copybara_wrong_subcommand() {
|
|
copybara_with_exit_code COMMAND_LINE_ERROR foooo
|
|
expect_log "Invalid subcommand 'foooo'. Available commands: "
|
|
expect_log "Try 'copybara help'"
|
|
}
|
|
|
|
function test_default_command_too_few_args() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR
|
|
expect_log "Configuration file missing for 'migrate' subcommand."
|
|
expect_log "Try 'copybara help'"
|
|
}
|
|
|
|
function test_migrate_missing_config() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR migrate
|
|
|
|
expect_log "Configuration file missing for 'migrate' subcommand."
|
|
expect_log "Try 'copybara help'"
|
|
}
|
|
|
|
function test_info_missing_config() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR info
|
|
|
|
expect_log "Configuration file missing for 'info' subcommand."
|
|
}
|
|
|
|
function test_info_too_many_arguments() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR info copy.bara.sky default foo
|
|
|
|
expect_log "Too many arguments for subcommand 'info'"
|
|
}
|
|
|
|
function test_validate_missing_config() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR validate
|
|
|
|
expect_log "Configuration file missing for 'validate' subcommand."
|
|
}
|
|
|
|
function test_validate_too_many_arguments() {
|
|
copybara_with_exit_code $COMMAND_LINE_ERROR validate copy.bara.sky default foo
|
|
|
|
expect_log "Too many arguments for subcommand 'validate'"
|
|
}
|
|
|
|
function test_validate_valid() {
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://foo/bar",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://bar/foo",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
mode = "ITERATIVE",
|
|
)
|
|
EOF
|
|
|
|
copybara validate copy.bara.sky
|
|
|
|
expect_log "Configuration '.*copy.bara.sky' is valid."
|
|
}
|
|
|
|
function test_validate_invalid() {
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflowFoo(
|
|
name = "default",
|
|
)
|
|
EOF
|
|
|
|
copybara_with_exit_code $CONFIGURATION_ERROR validate copy.bara.sky
|
|
|
|
expect_log "Configuration '.*copy.bara.sky' is invalid."
|
|
expect_log "Error loading config file"
|
|
}
|
|
|
|
function test_require_at_least_one_migration() {
|
|
cat > copy.bara.sky <<EOF
|
|
|
|
EOF
|
|
|
|
copybara_with_exit_code $CONFIGURATION_ERROR migrate copy.bara.sky
|
|
|
|
expect_log "At least one migration is required"
|
|
}
|
|
|
|
function test_apply_patch() {
|
|
prepare_glob_tree
|
|
( cd $remote || return
|
|
echo "patched" > test.java
|
|
echo "patched" > folder/test.java
|
|
git --no-pager diff > $workdir/../diff1.patch
|
|
run_git reset --hard
|
|
expect_in_file "foo" test.java
|
|
expect_in_file "foo" folder/test.java
|
|
echo "patched again" > folder/subfolder/test.java
|
|
git --no-pager diff > $workdir/../diff2.patch
|
|
run_git reset --hard
|
|
expect_in_file "foo" folder/subfolder/test.java
|
|
)
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
patch.apply(
|
|
patches = ["diff1.patch", "diff2.patch"],
|
|
)
|
|
],
|
|
)
|
|
EOF
|
|
copybara copy.bara.sky --force
|
|
( cd "$(mktemp -d)" || return
|
|
run_git clone $destination .
|
|
expect_in_file "patched" test.java
|
|
expect_in_file "patched" folder/test.java
|
|
expect_in_file "patched again" folder/subfolder/test.java
|
|
)
|
|
}
|
|
|
|
function test_description_migrator() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
commit_initial=$(single_file_commit "initial rev commit" file2.txt "initial")
|
|
commit_master=$(single_file_commit "last rev commit" file23.txt "origin")
|
|
commit_one=$(single_file_commit "c1 foooo origin/${commit_master} bar" file.txt "one")
|
|
|
|
popd || return
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
origin_files = glob(include = ["**"], exclude = ["file2.txt"]),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
transformations = [
|
|
metadata.map_references(
|
|
before = "origin/\${reference}",
|
|
after = "destination/\${reference}",
|
|
regex_groups = {
|
|
"before_ref": "[0-9a-f]+",
|
|
"after_ref": "[0-9a-f]+",
|
|
}
|
|
)
|
|
],
|
|
mode = "ITERATIVE",
|
|
)
|
|
EOF
|
|
|
|
copybara copy.bara.sky default $commit_master --last-rev $commit_initial
|
|
copybara copy.bara.sky default $commit_one --last-rev $commit_master
|
|
|
|
check_copybara_rev_id "$destination" "$commit_one"
|
|
|
|
( cd $destination || return
|
|
run_git log > $TEST_log
|
|
)
|
|
expect_log "c1 foooo destination/[a-f0-9]\{1,\} bar"
|
|
}
|
|
|
|
function test_invalid_last_rev() {
|
|
remote=$(temp_dir remote)
|
|
destination=$(empty_git_bare_repo)
|
|
|
|
pushd $remote || return
|
|
run_git init .
|
|
commit_initial=$(single_file_commit "initial rev commit" file2.txt "initial")
|
|
popd || return
|
|
|
|
cat > copy.bara.sky <<EOF
|
|
core.workflow(
|
|
name = "default",
|
|
origin = git.origin(
|
|
url = "file://$remote",
|
|
ref = "master",
|
|
),
|
|
origin_files = glob(include = ["**"], exclude = ["file2.txt"]),
|
|
destination = git.destination(
|
|
url = "file://$destination",
|
|
fetch = "master",
|
|
push = "master",
|
|
),
|
|
authoring = authoring.pass_thru("Copybara Team <no-reply@google.com>"),
|
|
mode = "ITERATIVE",
|
|
)
|
|
EOF
|
|
|
|
copybara_with_exit_code $CONFIGURATION_ERROR copy.bara.sky default --last-rev --some-other-flag
|
|
|
|
expect_log "Invalid refspec: --some-other-flag"
|
|
}
|
|
|
|
run_suite "Integration tests for Copybara code sharing tool."
|