diff --git a/test/e2e/list_diff_branch_range.rb b/test/e2e/list_diff_branch_range.rb new file mode 100644 index 0000000..e0e94a0 --- /dev/null +++ b/test/e2e/list_diff_branch_range.rb @@ -0,0 +1,60 @@ +# rubocop:disable all + +require_relative "../e2e" +require 'yaml' + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "branch = 'master' and change_in('/app')" + + - name: Test2 + run: + when: "branch = 'master' and change_in('/lib')" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.create_branch("dev") +origin.add_file("app/a.yml", "hello") +origin.commit!("Bootstrap app") + +origin.create_branch("feature-1") +origin.add_file("lib/a.yml", "hello") +origin.commit!("Bootstrap lib") + +repo = origin.clone_local_copy(branch: "feature-1") + +fixtures = { + '$SEMAPHORE_MERGE_BASE...$SEMAPHORE_GIT_SHA' => ['app/a.yml', 'lib/a.yml'], + '$SEMAPHORE_GIT_SHA^...$SEMAPHORE_GIT_SHA' => ['lib/a.yml'], + 'dev...$SEMAPHORE_GIT_SHA' => ['lib/a.yml'] +} + +fixtures.each do |branch_range, expected| + output = repo.run(%{ + export SEMAPHORE_GIT_SHA=$(git rev-parse HEAD) + export SEMAPHORE_GIT_BRANCH=feature-1 + + #{spc} list-diff --branch-range '#{branch_range}' > /tmp/output.txt + }) + + output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + + assert_eq($?.success?, true) + assert_eq(expected, output) +end + + diff --git a/test/e2e/list_diff_default_range.rb b/test/e2e/list_diff_default_range.rb new file mode 100644 index 0000000..9ca2f20 --- /dev/null +++ b/test/e2e/list_diff_default_range.rb @@ -0,0 +1,74 @@ +# rubocop:disable all + +require_relative "../e2e" +require 'yaml' + +# +# If the change_in is evaluated on the default branch, usually master branch, +# the commit range is the one provided by the git post commit hook. +# +# To configure this range, a developer can pass a default_range parameter to +# the function. +# +# The default value of this parameter is $SEMAPHORE_GIT_COMMIT_RANGE. +# + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "branch = 'master' and change_in('/app')" + + - name: Test2 + run: + when: "branch = 'master' and change_in('/lib')" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.add_file("app/a.yml", "hello") +origin.commit!("Changes in app") + +origin.add_file("lib/b.yml", "hello") +origin.commit!("Changes in lib") + +origin.add_file("test/c.yml", "hello") +origin.commit!("Changes in test") + +repo = origin.clone_local_copy(branch: "master") +repo.list_branches + +fixtures = { + 'HEAD~3..HEAD~1' => ['app/a.yml', 'lib/b.yml'], + 'HEAD~2..HEAD' => ['lib/b.yml', 'test/c.yml'], + 'HEAD~1..HEAD' => ['test/c.yml'] +} + +fixtures.each do |default_range, expected| + repo.run(%{ + export SEMAPHORE_GIT_COMMIT_RANGE="$(git rev-parse HEAD~2)...$(git rev-parse HEAD)" + echo "Passing $SEMAPHORE_GIT_COMMIT_RANGE to the compiler" + echo "" + + #{spc} list-diff --default-range #{default_range} > /tmp/output.txt + }) + + output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + + assert_eq($?.success?, true) + assert_eq(expected, output) +end + + + diff --git a/test/e2e/list_diff_missing_branch.rb b/test/e2e/list_diff_missing_branch.rb new file mode 100644 index 0000000..57ee678 --- /dev/null +++ b/test/e2e/list_diff_missing_branch.rb @@ -0,0 +1,46 @@ +# rubocop:disable all + +require_relative "../e2e" + +system "rm -f /tmp/output.yml" +system "rm -f /tmp/logs.jsonl" + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + skip: + when: "branch = 'master' and change_in('/lib', {default_branch: 'random'})" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.add_file("lib/A.txt", "hello") +origin.commit!("Changes on master") + +origin.create_branch("dev") +origin.add_file("lib/B.txt", "hello") +origin.commit!("Changes in dev") + +repo = origin.clone_local_copy(branch: "dev") +repo.run(%{#{spc} list-diff --default-branch random > /tmp/output.txt}, fail: false) + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.exitstatus, 1) +assert_eq(["Unknown git reference 'random'."], output) \ No newline at end of file diff --git a/test/e2e/list_diff_on_forked_prs.rb b/test/e2e/list_diff_on_forked_prs.rb new file mode 100644 index 0000000..c4ce810 --- /dev/null +++ b/test/e2e/list_diff_on_forked_prs.rb @@ -0,0 +1,83 @@ +# rubocop:disable all + +# +# This is not a full e2e test since it is impossible to completely immitate +# forked pull requests without GitHub. +# Instead, this tests replicates approach from test in *change_in_on_prs.rb* +# for testing regullar PRs but has several enivronment variables set in a way +# to indicate to change_in that it is a forked PR and that it should use a value +# of SEMAPHORE_GIT_COMMIT_RANGE env var as a range for git diff. +# + +require_relative "../e2e" +require 'yaml' + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "branch = 'master' and change_in('/app')" + + - name: Test2 + run: + when: "branch = 'master' and change_in('/lib')" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.create_branch("dev") +origin.add_file("app/a.yml", "hello") +origin.commit!("Change app/") + +origin.switch_branch("master") +origin.create_branch("forked-branch") +origin.add_file("lib/a.yml", "hello") +origin.commit!("Change lib/") + +origin.switch_branch("master") +origin.add_file("lib/b.yml", "world") +origin.commit!("Bootstrap lib") + +origin.merge_branch("dev") + +repo = origin.clone_local_copy(branch: "master") + +origin.run("git reset --hard HEAD~1") + +repo.run(%{ + export SEMAPHORE_GIT_SHA=$(git rev-parse HEAD) + + git reset --hard HEAD~1 + + git checkout $SEMAPHORE_GIT_SHA + + export SEMAPHORE_GIT_REF_TYPE=pull-request + export SEMAPHORE_GIT_BRANCH=master + export SEMAPHORE_GIT_PR_BRANCH=dev + + export SEMAPHORE_GIT_REPO_SLUG=renderedtext/test + export SEMAPHORE_GIT_PR_SLUG=forked-repo/test + export SEMAPHORE_GIT_COMMIT_RANGE=master...forked-branch + + git fetch origin +refs/heads/forked-branch:refs/heads/forked-branch + + #{spc} list-diff > /tmp/output.txt +}) + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.exitstatus, 0) +assert_eq(["lib/a.yml"], output) + + diff --git a/test/e2e/list_diff_on_prs.rb b/test/e2e/list_diff_on_prs.rb new file mode 100644 index 0000000..d5ed33c --- /dev/null +++ b/test/e2e/list_diff_on_prs.rb @@ -0,0 +1,80 @@ +# rubocop:disable all + +# +# In pipelines triggered by PRs Semaphore checkouts the merge commit as a +# detached head. That makes evaluating change_in tricky because merge commit +# includes changes made to target branch after the branch that is the source of +# the PR diverged from targeted branch. +# +# In order to get proper changeset we need to fetch both source and target +# branches, since target might not be master. +# After that we can use "...." as a commit +# range for change_in since it does not include changes made to target branch +# after the source branch diverged. +# +# This test simulates this state by creating repo with two branches, merging +# them and reseting HEAD of target branch back by one so merge commit becomes +# a detached head when it is checked out. +# + +require_relative "../e2e" +require 'yaml' + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "branch = 'master' and change_in('/app')" + + - name: Test2 + run: + when: "branch = 'master' and change_in('/lib')" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.create_branch("dev") +origin.add_file("app/a.yml", "hello") +origin.commit!("Bootstrap app") + +origin.switch_branch("master") +origin.add_file("lib/b.yml", "world") +origin.commit!("Bootstrap lib") + +origin.merge_branch("dev") + +repo = origin.clone_local_copy(branch: "master") + +origin.run(%{ + git reset --hard HEAD~1 +}) + +repo.run(%{ + export SEMAPHORE_GIT_SHA=$(git rev-parse HEAD) + + git reset --hard HEAD~1 + + git checkout $SEMAPHORE_GIT_SHA + + export SEMAPHORE_GIT_REF_TYPE=pull-request + export SEMAPHORE_GIT_BRANCH=master + export SEMAPHORE_GIT_PR_BRANCH=dev + + #{spc} list-diff > /tmp/output.txt +}) + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.exitstatus, 0) +assert_eq(["app/a.yml"], output) diff --git a/test/e2e/list_diff_on_tags.rb b/test/e2e/list_diff_on_tags.rb new file mode 100644 index 0000000..5236b4c --- /dev/null +++ b/test/e2e/list_diff_on_tags.rb @@ -0,0 +1,99 @@ +# rubocop:disable all + +require_relative "../e2e" +require 'yaml' + +# +# Change In on tags has no defined value. +# +# When the CI job is building a tag, there is no clear reference what is the +# base commit to which to compare the tag. +# +# However, the YAML file is probably the same for tags and regular branches. +# For this reason, the 'on_tags' parameter allows the developer to define +# what we are going to use a replacement when a tag is run. +# +# For example: +# +# change_in("/lib", {on_tags: true}) +# +# Will always be true on tags. The changes won't be calculated. +# +# The 'true' value is also the default value of this parameter. You can also +# set this value to 'false' with: +# +# change_in("/lib", {on_tags: false}) +# +# In which case the value will always be 'false' on tags. +# + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "branch = 'master' and change_in(['/app', 'log.txt'], {on_tags: true})" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" + + - name: Test2 + run: + when: "branch = 'master' and change_in('/app', {on_tags: false})" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" + + - name: Test3 + run: + when: "branch = 'master' and change_in('/app')" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.create_branch("dev") +origin.add_file("lib/B.txt", "hello") +origin.commit!("Changes in dev") + +repo = origin.clone_local_copy(branch: "dev") + +repo.run(%{ + export SEMAPHORE_GIT_REF_TYPE=tag + #{spc} list-diff > /tmp/output.txt +}) + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.exitstatus, 0) +assert_eq(["Running on a tag, skipping evaluation."], output) + +repo.run(%{ + export SEMAPHORE_GIT_REF_TYPE=branch + #{spc} list-diff > /tmp/output.txt +}) + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.exitstatus, 0) +assert_eq(["lib/B.txt"], output) \ No newline at end of file diff --git a/test/e2e/list_diff_semaphore_commit_range.rb b/test/e2e/list_diff_semaphore_commit_range.rb new file mode 100644 index 0000000..f451480 --- /dev/null +++ b/test/e2e/list_diff_semaphore_commit_range.rb @@ -0,0 +1,53 @@ +# rubocop:disable all + +require_relative "../e2e" +require 'yaml' + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "change_in('/app')" + + - name: Test2 + run: + when: "change_in('/lib')" + + - name: Test3 + run: + when: "change_in(['/lib'], {branch_range: '$SEMAPHORE_GIT_COMMIT_RANGE'})" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.add_file("lib/a.yml", "hello") +origin.commit!("Bootstrap lib") + +origin.create_branch("dev") +origin.add_file("app/a.yml", "hello") +origin.commit!("Bootstrap app") + +repo = origin.clone_local_copy(branch: "dev") +repo.run(%{ + export SEMAPHORE_GIT_SHA=$(git rev-parse HEAD) + export SEMAPHORE_GIT_COMMIT_RANGE=$(git rev-parse HEAD~2)...$(git rev-parse HEAD~1) + echo "SEMAPHORE_GIT_COMMIT_RANGE: $SEMAPHORE_GIT_COMMIT_RANGE" + + #{spc} list-diff --branch-range '$SEMAPHORE_GIT_COMMIT_RANGE' > /tmp/output.txt +}) + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.success?, true) +assert_eq(["lib/a.yml"], output) diff --git a/test/e2e/list_diff_simple.rb b/test/e2e/list_diff_simple.rb new file mode 100644 index 0000000..db1b11d --- /dev/null +++ b/test/e2e/list_diff_simple.rb @@ -0,0 +1,53 @@ +# rubocop:disable all + +require_relative "../e2e" +require 'yaml' + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + run: + when: "branch = 'master' and change_in('/lib')" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" + + - name: Test2 + run: + when: "branch = 'master' and change_in('/app')" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.add_file("lib/A.txt", "hello") +origin.commit!("Changes on master") + +origin.create_branch("dev") +origin.add_file("lib/B.txt", "hello") +origin.commit!("Changes in dev") + +repo = origin.clone_local_copy(branch: "dev") +repo.run("#{spc} list-diff > /tmp/output.txt") + +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.success?, true) +assert_eq(["lib/B.txt"], output) diff --git a/test/e2e/list_diff_with_default_branch.rb b/test/e2e/list_diff_with_default_branch.rb new file mode 100644 index 0000000..20b3dbf --- /dev/null +++ b/test/e2e/list_diff_with_default_branch.rb @@ -0,0 +1,81 @@ +# rubocop:disable all + +require_relative "../e2e" + +pipeline = %{ +version: v1.0 +name: Test +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Test + skip: + when: "branch = 'master' and change_in('/lib', {default_branch: 'master'})" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" + + - name: Test2 + skip: + when: "branch = 'master' and change_in('/lib', {default_branch: 'main'})" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" + + - name: Test3 + skip: + when: "branch = 'master' and change_in(['/lib', 'log.txt'], {default_branch: 'dev'})" + task: + jobs: + - name: Hello + commands: + - echo "Hello World" +} + +origin = TestRepoForChangeIn.setup() + +origin.add_file('.semaphore/semaphore.yml', pipeline) +origin.commit!("Bootstrap") + +origin.add_file("app/A.txt", "hello") +origin.commit!("Changes on master") + +origin.create_branch("main") +origin.add_file("lib/B.txt", "hello") +origin.commit!("Changes in main") + +origin.create_branch("dev") +origin.add_file("app/C.txt", "hello") +origin.commit!("Changes in dev") + +repo = origin.clone_local_copy(branch: "dev") + +repo.run("#{spc} list-diff --default-branch master > /tmp/output.txt") +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.success?, true) +assert_eq(["app/C.txt", "lib/B.txt"], output) + +repo.run("#{spc} list-diff --default-branch main > /tmp/output.txt") +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.success?, true) +assert_eq(["app/C.txt"], output) + +repo.run("#{spc} list-diff --default-branch dev > /tmp/output.txt") +output = File.readlines('/tmp/output.txt') + .map { |line| line.strip } + .reject { |line| line.empty? } + +assert_eq($?.success?, true) +assert_eq([], output)