docker_manager/spec/lib/git_repo_spec.rb

684 lines
22 KiB
Ruby

# frozen_string_literal: true
require "docker_manager/git_repo"
require_relative "../support/git_helpers"
RSpec.describe DockerManager::GitRepo do
describe ".find_all" do
subject(:all_repos) { described_class.find_all }
it "returns a list of repos" do
expect(all_repos).to be_present
end
it "contains the `docker_manager` and `discourse` repos" do
expect(all_repos.map(&:name)).to include("discourse", "docker_manager")
end
end
describe ".find" do
it "does not find invalid repos" do
expect(described_class.find(" NOT A REPO")).to be_blank
end
it "returns valid repos" do
repo = described_class.find_all.first
expect(repo.path).to be_present
end
end
context "with git repo" do
def shallow_clone?
clone_method == GitHelpers::CLONE_SHALLOW
end
def skip_for_shallow_clone
pending("Doesn't work on shallow clones") if shallow_clone?
end
subject(:repo) do
prepare_repos
repo = described_class.new(@local_repo.path)
repo.update_remote! unless @skip_update_remote
repo
end
let!(:initial_branch) { "main" }
before do
@skip_update_remote = false
@local_repo = @remote_git_repo = nil
@before_local_repo_clone = []
@after_local_repo_clone = []
end
after { @remote_git_repo.destroy }
def prepare_repos
return if @local_repo && @remote_git_repo
@remote_git_repo = GitHelpers::RemoteGitRepo.new(initial_branch: initial_branch)
@remote_git_repo.commit(
filename: "foo.txt",
commits: [
{ content: "A", date: "2023-03-06T20:31:17Z" },
{
content: "B",
date: "2023-03-06T21:08:52Z",
tags: %w[v3.1.0.beta1 beta latest-release],
},
{ content: "C", date: "2023-03-06T22:48:29Z" },
],
)
@remote_git_repo.create_branches("tests-passed")
@before_local_repo_clone.each { |callback| callback.call }
@local_repo = @remote_git_repo.create_local_clone(method: clone_method)
@after_local_repo_clone.each { |callback| callback.call }
@remote_git_repo.in_remote_repo { |git| git.call("log --pretty=oneline") }
end
def add_new_commits
@remote_git_repo.commit(
filename: "foo.txt",
commits: [
{ content: "D", date: "2023-03-07T10:11:19Z" },
{
content: "E",
date: "2023-03-07T12:58:29Z",
tags: %w[v3.1.0.beta2 beta latest-release],
},
{ content: "F", date: "2023-03-07T15:22:23Z" },
],
)
@remote_git_repo.rebase(source_branch: "main", target_branch: "tests-passed")
end
shared_examples "common tests" do
context "when tracking `tests-passed` branch" do
before { @after_local_repo_clone << -> { @local_repo.checkout("tests-passed") } }
describe "#has_local_main?" do
context "with existing `main` branch" do
let(:initial_branch) { "main" }
it "detects the branch" do
expect(repo.has_local_main?).to eq(true)
end
end
context "with missing `main` branch" do
let(:initial_branch) { "master" }
it "doesn't detect the branch" do
expect(repo.has_local_main?).to eq(false)
end
end
end
describe "#tracking_ref" do
it "returns the correct remote branch" do
expect(repo.tracking_ref).to eq("origin/tests-passed")
end
context "with `master` as initial branch" do
let(:initial_branch) { "master" }
before { @after_local_repo_clone << -> { @local_repo.checkout("master") } }
it "returns `origin/master` if a repo hasn't been renamed" do
expect(repo.tracking_ref).to eq("origin/master")
end
it "returns `origin/main` if a repo has been renamed but still tracks `master`" do
@after_local_repo_clone << -> do
@remote_git_repo.rename_branch(old_name: "master", new_name: "main")
end
expect(repo.tracking_ref).to eq("origin/main")
end
end
context "with `main` as current local branch" do
before { @after_local_repo_clone << -> { @local_repo.checkout("main") } }
it "returns `origin/main` if a repo points at `origin/main`" do
expect(repo.tracking_ref).to eq("origin/main")
end
end
end
describe "#upstream_branch" do
it "returns the correct branch name" do
expect(repo.upstream_branch).to eq("origin/tests-passed")
end
context "with `master` as initial branch" do
let(:initial_branch) { "master" }
before { @after_local_repo_clone << -> { @local_repo.checkout("master") } }
it "returns `origin/master` if a repo hasn't been renamed" do
expect(repo.upstream_branch).to eq("origin/master")
end
it "returns `origin/master` if a repo has been renamed but still tracks `master`" do
@after_local_repo_clone << -> do
@remote_git_repo.rename_branch(old_name: "master", new_name: "main")
end
expect(repo.upstream_branch).to eq("origin/master")
end
end
context "with `main` as current local branch" do
before { @after_local_repo_clone << -> { @local_repo.checkout("main") } }
it "returns `origin/main` if a repo points at `origin/main`" do
expect(repo.upstream_branch).to eq("origin/main")
end
end
end
describe "#upstream_branch_exist?" do
it "returns true when upstream branch exist" do
expect(repo.upstream_branch_exist?).to eq(true)
end
it "returns false when upstream branch doesn't exist" do
@after_local_repo_clone << -> { @remote_git_repo.delete_branches("tests-passed") }
expect(repo.upstream_branch_exist?).to eq(false)
end
end
describe "#detached_head?" do
it "returns false" do
expect(repo.detached_head?).to eq(false)
end
end
context "when local clone and origin are the same" do
describe "#latest_local_commit" do
it "returns the correct commit hash" do
expect(repo.latest_local_commit).to eq("16a1d8111ff1eb6e8fc1d1b973b4fd92cacbebcc")
end
end
describe "#latest_origin_commit" do
it "returns the correct commit hash" do
expect(repo.latest_origin_commit).to eq("16a1d8111ff1eb6e8fc1d1b973b4fd92cacbebcc")
end
end
describe "#latest_local_commit_date" do
it "returns the correct commit date" do
expect(repo.latest_local_commit_date).to eq("2023-03-06T22:48:29Z")
end
end
describe "#latest_origin_commit_date" do
it "returns the correct commit date" do
expect(repo.latest_origin_commit_date).to eq("2023-03-06T22:48:29Z")
end
end
context "with no tags" do
before do
@before_local_repo_clone << -> do
@remote_git_repo.delete_tags("beta", "latest-release", "v3.1.0.beta1")
end
end
describe "#latest_local_tag_version" do
it "returns nil as version" do
expect(repo.latest_local_tag_version).to be_nil
end
end
describe "#latest_origin_tag_version" do
it "returns nil as version" do
expect(repo.latest_origin_tag_version).to be_nil
end
end
end
context "with `beta`, `latest-release` and version tags on HEAD~1" do
before { skip_for_shallow_clone }
describe "#latest_local_tag_version" do
it "returns the correct version and ignores the `beta` tag" do
expect(repo.latest_local_tag_version).to eq("latest-release +1")
end
end
describe "#latest_origin_tag_version" do
it "returns the correct version and ignores the `beta` and `latest-release` tags" do
expect(repo.latest_origin_tag_version).to eq("v3.1.0.beta1 +1")
end
end
end
context "with `beta` and version tags on HEAD~1" do
before do
skip_for_shallow_clone
@before_local_repo_clone << -> { @remote_git_repo.delete_tags("latest-release") }
end
describe "#latest_local_tag_version" do
it "returns the correct version and ignores the `beta` tag" do
expect(repo.latest_local_tag_version).to eq("v3.1.0.beta1 +1")
end
end
describe "#latest_origin_tag_version" do
it "returns the correct version and ignores the `beta` tag" do
expect(repo.latest_origin_tag_version).to eq("v3.1.0.beta1 +1")
end
end
end
describe "#commits_behind" do
it "returns 0 because local and origin are the same" do
expect(repo.commits_behind).to eq(0)
end
end
describe "#update_remote!" do
it "fetches the correct amount of new commits" do
prepare_repos
expect { repo.update_remote! }.to not_change { @local_repo.commit_count }
end
end
end
context "when origin has new commits" do
before { @after_local_repo_clone << method(:add_new_commits) }
describe "#latest_local_commit" do
it "returns the correct commit hash" do
expect(repo.latest_local_commit).to eq("16a1d8111ff1eb6e8fc1d1b973b4fd92cacbebcc")
end
end
describe "#latest_origin_commit" do
it "returns the correct commit hash" do
expect(repo.latest_origin_commit).to eq("44b4ef6472e902d767335c4b19d47fd7a079d7c3")
end
end
describe "#latest_local_commit_date" do
it "returns the correct commit date" do
expect(repo.latest_local_commit_date).to eq("2023-03-06T22:48:29Z")
end
end
describe "#latest_origin_commit_date" do
it "returns the correct commit date" do
expect(repo.latest_origin_commit_date).to eq("2023-03-07T15:22:23Z")
end
end
describe "#latest_local_tag_version" do
it "returns the correct version" do
skip_for_shallow_clone
expect(repo.latest_local_tag_version).to eq("v3.1.0.beta1 +1")
end
end
describe "#latest_origin_tag_version" do
it "returns the correct version and ignores the `beta` and `latest-release` tags" do
skip_for_shallow_clone
expect(repo.latest_origin_tag_version).to eq("v3.1.0.beta2 +1")
end
end
describe "#commits_behind" do
it "returns the correct number of commits" do
skip_for_shallow_clone
expect(repo.commits_behind).to eq(3)
end
end
describe "#update_remote!" do
it "fetches the correct amount of new commits" do
prepare_repos
expect { repo.update_remote! }.to change { @local_repo.commit_count }.by(
fetch_commit_count,
)
end
end
end
end
context "when tracking `beta` tag" do
before do
@after_local_repo_clone << -> do
unless shallow_clone?
@local_repo.checkout("beta")
# Mimics the behavior of `web.template.yml` where we store the value of the `$version` variable
# as a user-defined config value in git.
# See https://github.com/discourse/discourse_docker/blob/main/templates/web.template.yml
@local_repo.git("config user.discourse-version beta")
end
end
end
describe "#has_local_main?" do
context "with existing `main` branch" do
let(:initial_branch) { "main" }
it "detects the branch" do
expect(repo.has_local_main?).to eq(true)
end
end
context "with missing `main` branch" do
let(:initial_branch) { "master" }
it "doesn't detect the branch" do
expect(repo.has_local_main?).to eq(false)
end
end
end
describe "#tracking_ref" do
it "returns the correct remote branch" do
skip_for_shallow_clone
expect(repo.tracking_ref).to eq("beta")
end
end
describe "#upstream_branch" do
it "doesn't return a branch name" do
skip_for_shallow_clone
expect(repo.upstream_branch).to be_nil
end
end
describe "#upstream_branch_exist?" do
it "returns false because we aren't tracking a branch" do
skip_for_shallow_clone
expect(repo.upstream_branch_exist?).to eq(false)
end
end
describe "#detached_head?" do
it "returns true" do
skip_for_shallow_clone
expect(repo.detached_head?).to eq(true)
end
end
context "when local clone and origin are the same" do
describe "#latest_local_commit" do
it "returns the correct commit hash" do
skip_for_shallow_clone
expect(repo.latest_local_commit).to eq("e43b6978c22ea3aeafbcf96c6e4fff5af0b7da29")
end
end
describe "#latest_origin_commit" do
it "returns the correct commit hash" do
skip_for_shallow_clone
expect(repo.latest_origin_commit).to eq("e43b6978c22ea3aeafbcf96c6e4fff5af0b7da29")
end
end
describe "#latest_local_commit_date" do
it "returns the correct commit date" do
skip_for_shallow_clone
expect(repo.latest_local_commit_date).to eq("2023-03-06T21:08:52Z")
end
end
describe "#latest_origin_commit_date" do
it "returns the correct commit date" do
skip_for_shallow_clone
expect(repo.latest_origin_commit_date).to eq("2023-03-06T21:08:52Z")
end
end
describe "#latest_local_tag_version" do
it "returns the correct version and ignores the `beta` tag" do
skip_for_shallow_clone
expect(repo.latest_local_tag_version).to eq("latest-release")
end
end
describe "#latest_origin_tag_version" do
it "returns the correct version and ignores the `beta` and `latest-release` tags" do
skip_for_shallow_clone
expect(repo.latest_origin_tag_version).to eq("v3.1.0.beta1")
end
end
describe "#commits_behind" do
it "returns 0 because local and origin are the same" do
expect(repo.commits_behind).to eq(0)
end
end
describe "#update_remote!" do
it "fetches the correct amount of new commits" do
prepare_repos
expect { repo.update_remote! }.to not_change { @local_repo.commit_count }
end
end
end
context "when origin has new commits" do
before { @after_local_repo_clone << method(:add_new_commits) }
describe "#latest_local_commit" do
it "returns the correct commit hash" do
skip_for_shallow_clone
expect(repo.latest_local_commit).to eq("e43b6978c22ea3aeafbcf96c6e4fff5af0b7da29")
end
end
describe "#latest_origin_commit" do
it "returns the correct commit hash" do
skip_for_shallow_clone
expect(repo.latest_origin_commit).to eq("bebd76be58db951fac6abc8d4d0746951fcd1082")
end
end
describe "#latest_local_commit_date" do
it "returns the correct commit date" do
skip_for_shallow_clone
expect(repo.latest_local_commit_date).to eq("2023-03-06T21:08:52Z")
end
end
describe "#latest_origin_commit_date" do
it "returns the correct commit date" do
skip_for_shallow_clone
expect(repo.latest_origin_commit_date).to eq("2023-03-07T12:58:29Z")
end
end
describe "#latest_local_tag_version" do
it "returns the correct version" do
skip_for_shallow_clone
expect(repo.latest_local_tag_version).to eq("v3.1.0.beta1")
end
end
describe "#latest_origin_tag_version" do
it "returns the correct version" do
skip_for_shallow_clone
expect(repo.latest_origin_tag_version).to eq("v3.1.0.beta2")
end
end
describe "#commits_behind" do
it "returns the correct number of commits" do
skip_for_shallow_clone
expect(repo.commits_behind).to eq(3)
end
end
describe "#update_remote!" do
it "fetches the correct amount of new commits" do
prepare_repos
expect { repo.update_remote! }.to change { @local_repo.commit_count }.by(
fetch_commit_count,
)
end
end
end
end
context "when tracking deleted branch" do
before do
@before_local_repo_clone << -> do
@remote_git_repo.delete_tags("beta")
@remote_git_repo.create_branches("beta")
end
@after_local_repo_clone << -> do
@local_repo.checkout("beta")
@remote_git_repo.delete_branches("beta")
@remote_git_repo.in_working_directory do |git|
git.call("tag -m 'latest beta release' -a beta latest-release^{}")
git.call("push origin beta")
end
end
end
describe "#latest_local_commit" do
it "returns the correct commit hash" do
expect(repo.latest_local_commit).to eq("16a1d8111ff1eb6e8fc1d1b973b4fd92cacbebcc")
end
end
describe "#latest_origin_commit" do
it "returns the correct commit hash" do
skip_for_shallow_clone
expect(repo.latest_origin_commit).to be_nil
end
end
describe "#latest_local_commit_date" do
it "returns the correct commit date" do
expect(repo.latest_local_commit_date).to eq("2023-03-06T22:48:29Z")
end
end
describe "#latest_origin_commit_date" do
it "returns the correct commit date" do
skip_for_shallow_clone
expect(repo.latest_origin_commit_date).to be_nil
end
end
describe "#latest_local_tag_version" do
it "returns the correct version" do
skip_for_shallow_clone
expect(repo.latest_local_tag_version).to eq("latest-release +1")
end
end
describe "#latest_origin_tag_version" do
it "returns the correct version and ignores the `beta` and `latest-release` tags" do
expect(repo.latest_origin_tag_version).to be_nil
end
end
describe "#commits_behind" do
it "returns the correct number of commits" do
expect(repo.commits_behind).to eq(0)
end
end
end
describe "#url" do
before do
@skip_update_remote = true
@after_local_repo_clone << -> { @local_repo.git("remote set-url origin #{remote_url}") }
end
context "with GitHub HTTPS URL" do
let(:remote_url) { "https://github.com/discourse/example.git" }
it "returns the unmodified URL" do
expect(repo.url).to eq("https://github.com/discourse/example.git")
end
end
context "with GitHub SSH URL" do
let(:remote_url) { "git@github.com:discourse/example.git" }
it "returns a HTTPS URL" do
expect(repo.url).to eq("https://github.com/discourse/example.git")
end
end
context "with a different HTTPS URL" do
let(:remote_url) { "https://example.com/discourse.git" }
it "returns the unmodified URL" do
expect(repo.url).to eq("https://example.com/discourse.git")
end
end
context "with a different SSH URL" do
let(:remote_url) { "git@example.com:discourse.git" }
it "returns the unmodified URL" do
expect(repo.url).to eq("git@example.com:discourse.git")
end
end
end
end
context "with full clone" do
let!(:clone_method) { GitHelpers::CLONE_FULL }
let(:fetch_commit_count) { 3 }
include_examples "common tests"
end
context "with shallow clone" do
let!(:clone_method) { GitHelpers::CLONE_SHALLOW }
let(:fetch_commit_count) { 1 }
include_examples "common tests"
end
context "with partial (treeless) clone" do
let!(:clone_method) { GitHelpers::CLONE_TREELESS }
let(:fetch_commit_count) { 3 }
include_examples "common tests"
end
context "with modern discourse-compatibility file" do
let!(:clone_method) { GitHelpers::CLONE_SHALLOW }
before do
@before_local_repo_clone << -> do
@remote_git_repo.commit(
filename: ".discourse-compatibility",
commits: [{ content: "<= 1000.0.0: twoPointFiveBranch", date: "2023-03-06T20:31:17Z" }],
)
end
end
it "works" do
DockerManager::FallbackCompatibilityParser.expects(:find_compatible_resource).never
expect(repo.tracking_ref).to eq("twoPointFiveBranch")
end
it "works even in old core" do
Discourse
.stubs(:find_compatible_resource)
.with { |version_list| version_list.include?("<=") }
.raises(ArgumentError)
expect(repo.tracking_ref).to eq("twoPointFiveBranch")
end
end
end
end