Compare commits
4 Commits
staging
...
m0ses_tc_m
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
e320480935
|
||
|
|
f5a32792e0 | ||
| b4945a8ae4 | |||
| 1451266ddc |
@@ -44,42 +44,6 @@ type NewRepos struct {
|
||||
const maintainership_line = "MAINTAINER"
|
||||
|
||||
var true_lines []string = []string{"1", "TRUE", "YES", "OK", "T"}
|
||||
var InvalidUrlError error = errors.New("PrjGit or PackageGit URLs cannot be empty.")
|
||||
var AbsoluteUrlError error = errors.New("PrjGit or PackageGit URLs cannot be relative.")
|
||||
var HostsNotEqualError error = errors.New("PrjGit or PackageGit are not the same hosts.")
|
||||
var AbsoluteUrlWithQuery error = errors.New("PrjGit or PackageGit with query parameter. Unsupported.")
|
||||
var InvalidPath error = errors.New("PrjGit or PackageGit path has unsupported format.")
|
||||
|
||||
|
||||
func RelativeRepositoryPath(prjgit_org, packagegit string) (string, error) {
|
||||
if len(packagegit) == 0 {
|
||||
return "", InvalidUrlError
|
||||
}
|
||||
|
||||
pkggiturl, err := url.Parse(packagegit)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !pkggiturl.IsAbs() {
|
||||
return "", AbsoluteUrlError
|
||||
}
|
||||
if len(pkggiturl.RawQuery) != 0 {
|
||||
return "", AbsoluteUrlWithQuery
|
||||
}
|
||||
|
||||
pkggitpath := SplitStringNoEmpty(pkggiturl.Path, "/")
|
||||
|
||||
if len(pkggitpath) != 2 {
|
||||
return "", InvalidPath
|
||||
}
|
||||
|
||||
pkggitpath[1] = strings.TrimSuffix(pkggitpath[1], ".git")
|
||||
if prjgit_org == pkggitpath[0] {
|
||||
return "../" + pkggitpath[1], nil
|
||||
}
|
||||
|
||||
return "../../" + pkggitpath[0] + "/" + pkggitpath[1], nil
|
||||
}
|
||||
|
||||
func HasSpace(s string) bool {
|
||||
return strings.IndexFunc(s, unicode.IsSpace) >= 0
|
||||
|
||||
@@ -8,82 +8,6 @@ import (
|
||||
"src.opensuse.org/autogits/common"
|
||||
)
|
||||
|
||||
func TestRelativeRepositoryPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prjorg, repo string
|
||||
|
||||
hasError bool
|
||||
relative string
|
||||
}{
|
||||
{
|
||||
name: "Empty packagegit",
|
||||
prjorg: "org1",
|
||||
repo: "",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid URL",
|
||||
prjorg: "org1",
|
||||
repo: ":",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Relative packagegit",
|
||||
prjorg: "org1",
|
||||
repo: "/path/to/repo",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Packagegit with query",
|
||||
prjorg: "org1",
|
||||
repo: "https://host/org1/repo?query=1",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid path (too short)",
|
||||
prjorg: "org1",
|
||||
repo: "https://host/repo",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid path (too long)",
|
||||
prjorg: "org1",
|
||||
repo: "https://host/org/repo/extra",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Same org",
|
||||
prjorg: "org1",
|
||||
repo: "https://host/org1/repo.git",
|
||||
relative: "../repo",
|
||||
},
|
||||
{
|
||||
name: "Different org",
|
||||
prjorg: "org1",
|
||||
repo: "https://host/org2/repo.git",
|
||||
relative: "../../org2/repo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
r, err := common.RelativeRepositoryPath(test.prjorg, test.repo)
|
||||
|
||||
if err != nil && !test.hasError {
|
||||
t.Error("Expected no error but have one", err)
|
||||
}
|
||||
if err == nil && test.hasError {
|
||||
t.Error("Expected an error but had none. Returned:", r)
|
||||
}
|
||||
|
||||
if err == nil && test.relative != r {
|
||||
t.Error("Expected", test.relative, "but have", r)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitUrlParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -61,6 +61,12 @@ BRANCH_CONFIG_CUSTOM = {
|
||||
"Reviewers": ["+usera", "+userb", "-autogits_obs_staging_bot"]
|
||||
}
|
||||
},
|
||||
"manual-multipkg-merge": {
|
||||
"workflow.config": {
|
||||
"ManualMergeOnly": True,
|
||||
"Reviewers": ["+usera", "+ownerB", "-autogits_obs_staging_bot"]
|
||||
}
|
||||
},
|
||||
"label-test": {
|
||||
"workflow.config": {
|
||||
"ManualMergeProject": True,
|
||||
@@ -274,6 +280,10 @@ def staging_main_env(gitea_env):
|
||||
def manual_merge_env(gitea_env):
|
||||
return gitea_env, "myproducts/mySLFO", "manual-merge"
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def manual_multipkg_merge_env(gitea_env):
|
||||
return gitea_env, "myproducts/mySLFO", "manual-multipkg-merge"
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def maintainer_env(gitea_env):
|
||||
return gitea_env, "myproducts/mySLFO", "maintainer-merge"
|
||||
@@ -327,5 +337,6 @@ def test_user_client(gitea_env):
|
||||
username = f"test-user-{int(time.time())}"
|
||||
gitea_env.create_user(username, "password123", f"{username}@example.com")
|
||||
gitea_env.add_collaborator("mypool", "pkgA", username, "write")
|
||||
gitea_env.add_collaborator("mypool", "pkgB", username, "write")
|
||||
gitea_env.add_collaborator("myproducts", "mySLFO", username, "write")
|
||||
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo=username)
|
||||
|
||||
@@ -65,7 +65,7 @@ index 0000000..e69de29
|
||||
print("Waiting for all expected review requests and approving them...")
|
||||
# Expected reviewers based on manual-merge branch config and pkgA maintainership
|
||||
expected_reviewers = {"usera", "userb", "ownerA", "ownerX", "ownerY"}
|
||||
|
||||
|
||||
# ManualMergeOnly still requires regular reviews to be satisfied.
|
||||
# We poll until all expected reviewers are requested, then approve them.
|
||||
all_requested = False
|
||||
@@ -73,7 +73,7 @@ index 0000000..e69de29
|
||||
# Trigger approvals for whatever is already requested
|
||||
gitea_env.approve_requested_reviews("mypool/pkgA", package_pr_number)
|
||||
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
|
||||
# Explicitly handle staging bot if it is requested or pending
|
||||
prj_reviews = gitea_env.list_reviews("myproducts/mySLFO", project_pr_number)
|
||||
if any(r["user"]["login"] == "autogits_obs_staging_bot" and r["state"] in ["REQUEST_REVIEW", "PENDING"] for r in prj_reviews):
|
||||
@@ -83,7 +83,7 @@ index 0000000..e69de29
|
||||
# Check if all expected reviewers have at least one review record (any state)
|
||||
pkg_reviews = gitea_env.list_reviews("mypool/pkgA", package_pr_number)
|
||||
current_reviewers = {r["user"]["login"] for r in pkg_reviews}
|
||||
|
||||
|
||||
if expected_reviewers.issubset(current_reviewers):
|
||||
# Also ensure they are all approved (not just requested)
|
||||
approved_reviewers = {r["user"]["login"] for r in pkg_reviews if r["state"] == "APPROVED"}
|
||||
@@ -94,13 +94,13 @@ index 0000000..e69de29
|
||||
all_requested = True
|
||||
print(f"All expected reviewers {expected_reviewers} and staging bot have approved.")
|
||||
break
|
||||
|
||||
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
|
||||
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
|
||||
assert not pkg_details.get("merged"), "Package PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
assert not prj_details.get("merged"), "Project PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
assert all_requested, f"Timed out waiting for all expected reviewers {expected_reviewers} to approve. Current: {current_reviewers}"
|
||||
@@ -114,29 +114,259 @@ index 0000000..e69de29
|
||||
print("Polling for PR merge status...")
|
||||
package_merged = False
|
||||
project_merged = False
|
||||
|
||||
|
||||
for i in range(20): # Poll for up to 20 seconds
|
||||
if not package_merged:
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
|
||||
if pkg_details.get("merged"):
|
||||
package_merged = True
|
||||
print(f"Package PR mypool/pkgA#{package_pr_number} merged.")
|
||||
|
||||
|
||||
if not project_merged:
|
||||
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
|
||||
if prj_details.get("merged"):
|
||||
project_merged = True
|
||||
print(f"Project PR myproducts/mySLFO#{project_pr_number} merged.")
|
||||
|
||||
|
||||
if package_merged and project_merged:
|
||||
break
|
||||
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
assert package_merged, f"Package PR mypool/pkgA#{package_pr_number} was not merged after 'merge ok'."
|
||||
assert project_merged, f"Project PR myproducts/mySLFO#{project_pr_number} was not merged after 'merge ok'."
|
||||
print("Both PRs merged successfully after 'merge ok'.")
|
||||
|
||||
@pytest.mark.t003
|
||||
def test_003_refuse_manual_merge(manual_merge_env, test_user_client, ownerB_client, staging_bot_client):
|
||||
"""
|
||||
Test scenario TC-MERGE-003:
|
||||
1. Create a PackageGit PR with ManualMergeOnly set to true.
|
||||
2. Ensure all mandatory reviews are completed on both project and package PRs.
|
||||
3. Comment "merge ok" on the package PR from the account of a not requested reviewer.
|
||||
4. Verify the PR is not merged.
|
||||
"""
|
||||
gitea_env, test_full_repo_name, merge_branch_name = manual_merge_env
|
||||
|
||||
# 1. Create a package PR
|
||||
diff = """diff --git a/manual_merge_test.txt b/manual_merge_test.txt
|
||||
new file mode 100644
|
||||
index 0000000..e69de29
|
||||
"""
|
||||
print(f"--- Creating package PR in mypool/pkgA on branch {merge_branch_name} ---")
|
||||
package_pr = test_user_client.create_gitea_pr("mypool/pkgA", diff, "Test Manual Merge Fixture", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
print(f"Created package PR mypool/pkgA#{package_pr_number}")
|
||||
|
||||
# 2. Make sure the workflow-pr service created related project PR
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgA", package_pr_number)
|
||||
assert project_pr_number is not None, "Workflow bot did not create a project PR."
|
||||
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
|
||||
|
||||
# 3. Approve reviews and verify NOT merged
|
||||
print("Waiting for all expected review requests and approving them...")
|
||||
# Expected reviewers based on manual-merge branch config and pkgA maintainership
|
||||
expected_reviewers = {"usera", "userb", "ownerA", "ownerX", "ownerY"}
|
||||
|
||||
# ManualMergeOnly still requires regular reviews to be satisfied.
|
||||
# We poll until all expected reviewers are requested, then approve them.
|
||||
all_requested = False
|
||||
for _ in range(30):
|
||||
# Trigger approvals for whatever is already requested
|
||||
gitea_env.approve_requested_reviews("mypool/pkgA", package_pr_number)
|
||||
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
# Explicitly handle staging bot if it is requested or pending
|
||||
prj_reviews = gitea_env.list_reviews("myproducts/mySLFO", project_pr_number)
|
||||
if any(r["user"]["login"] == "autogits_obs_staging_bot" and r["state"] in ["REQUEST_REVIEW", "PENDING"] for r in prj_reviews):
|
||||
print("Staging bot has a pending/requested review. Approving...")
|
||||
staging_bot_client.create_review("myproducts/mySLFO", project_pr_number, event="APPROVED", body="Staging bot approves")
|
||||
|
||||
# Check if all expected reviewers have at least one review record (any state)
|
||||
pkg_reviews = gitea_env.list_reviews("mypool/pkgA", package_pr_number)
|
||||
current_reviewers = {r["user"]["login"] for r in pkg_reviews}
|
||||
|
||||
if expected_reviewers.issubset(current_reviewers):
|
||||
# Also ensure they are all approved (not just requested)
|
||||
approved_reviewers = {r["user"]["login"] for r in pkg_reviews if r["state"] == "APPROVED"}
|
||||
if expected_reviewers.issubset(approved_reviewers):
|
||||
# And check project PR for bot approval
|
||||
prj_approved = any(r["user"]["login"] == "autogits_obs_staging_bot" and r["state"] == "APPROVED" for r in prj_reviews)
|
||||
if prj_approved:
|
||||
all_requested = True
|
||||
print(f"All expected reviewers {expected_reviewers} and staging bot have approved.")
|
||||
break
|
||||
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
|
||||
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
assert not pkg_details.get("merged"), "Package PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
assert not prj_details.get("merged"), "Project PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
assert all_requested, f"Timed out waiting for all expected reviewers {expected_reviewers} to approve. Current: {current_reviewers}"
|
||||
print("Both PRs have all required approvals but are not merged (as expected with ManualMergeOnly).")
|
||||
|
||||
# 4. Comment "merge ok" from a requested reviewer (ownerB)
|
||||
print("Commenting 'merge ok' on package PR as user ownerB ...")
|
||||
ownerB_client.create_issue_comment("mypool/pkgA", package_pr_number, "merge ok")
|
||||
|
||||
# 5. Verify both PRs are merged
|
||||
print("Polling for PR merge status...")
|
||||
package_merged = False
|
||||
project_merged = False
|
||||
|
||||
for i in range(20): # Poll for up to 20 seconds
|
||||
if not package_merged:
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
|
||||
if pkg_details.get("merged"):
|
||||
package_merged = True
|
||||
print(f"Package PR mypool/pkgA#{package_pr_number} merged.")
|
||||
|
||||
if not project_merged:
|
||||
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
|
||||
if prj_details.get("merged"):
|
||||
project_merged = True
|
||||
print(f"Project PR myproducts/mySLFO#{project_pr_number} merged.")
|
||||
|
||||
if package_merged and project_merged:
|
||||
break
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
assert not package_merged, f"Package PR mypool/pkgA#{package_pr_number} was merged after 'merge ok'."
|
||||
assert not project_merged, f"Project PR myproducts/mySLFO#{project_pr_number} was merged after 'merge ok'."
|
||||
print("Both PRs merged not after 'merge ok'.")
|
||||
|
||||
@pytest.mark.t004
|
||||
def test_004_multiple_packages_manual_merge(manual_multipkg_merge_env, test_user_client, usera_client, ownerB_client, staging_bot_client):
|
||||
|
||||
"""
|
||||
Test scenario TC-MERGE-004:
|
||||
|
||||
1. Create a ProjectGit PR that references multiple PackageGit PRs with ManualMergeOnly set to true.
|
||||
2. Ensure all mandatory reviews are completed on both project and package PRs.
|
||||
3. Comment merge ok on each package PR from the account of a package maintainer.
|
||||
4. Verify PR is merged only after merge ok is commented on all associated PackageGit PRs.
|
||||
"""
|
||||
gitea_env, test_full_repo_name, merge_branch_name = manual_multipkg_merge_env
|
||||
|
||||
# 1. Create a package PR
|
||||
diff = """diff --git a/manual_merge_test.txt b/manual_merge_test.txt
|
||||
new file mode 100644
|
||||
index 0000000..e69de29
|
||||
"""
|
||||
print(f"--- Creating package PR in mypool/pkgA on branch {merge_branch_name} ---")
|
||||
package_pr1 = test_user_client.create_gitea_pr("mypool/pkgA", diff, "Test Manual Merge Fixture (MultiPKG)", False, base_branch=merge_branch_name)
|
||||
package_pr1_number = package_pr1["number"]
|
||||
print(f"Created package PR mypool/pkgA#{package_pr1_number}")
|
||||
|
||||
print(f"--- Creating package PR in mypool/pkgB on branch {merge_branch_name} ---")
|
||||
package_pr2 = test_user_client.create_gitea_pr("mypool/pkgB", diff, "Test Manual Merge Fixture (MultiPKG)", False, base_branch=merge_branch_name)
|
||||
package_pr2_number = package_pr2["number"]
|
||||
print(f"Created package PR mypool/pkgB#{package_pr2_number}")
|
||||
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgA", package_pr1_number)
|
||||
assert project_pr_number is not None, "Workflow bot did not create a project PR (pkgA)."
|
||||
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
|
||||
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgB", package_pr2_number)
|
||||
assert project_pr_number is not None, "Workflow bot did not create a project PR (pkgB)."
|
||||
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
|
||||
|
||||
# 3. Approve reviews and verify NOT merged
|
||||
print("Waiting for all expected review requests and approving them...")
|
||||
# Expected reviewers based on manual-merge branch config and pkgA maintainership
|
||||
expected_reviewers = {"usera", "userb", "ownerA", "ownerX", "ownerY"}
|
||||
|
||||
# ManualMergeOnly still requires regular reviews to be satisfied.
|
||||
# We poll until all expected reviewers are requested, then approve them.
|
||||
all_requested = False
|
||||
for _ in range(30):
|
||||
# Trigger approvals for whatever is already requested
|
||||
gitea_env.approve_requested_reviews("mypool/pkgA", package_pr1_number)
|
||||
gitea_env.approve_requested_reviews("mypool/pkgB", package_pr2_number)
|
||||
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
# Explicitly handle staging bot if it is requested or pending
|
||||
prj_reviews = gitea_env.list_reviews("myproducts/mySLFO", project_pr_number)
|
||||
if any(r["user"]["login"] == "autogits_obs_staging_bot" and r["state"] in ["REQUEST_REVIEW", "PENDING"] for r in prj_reviews):
|
||||
print("Staging bot has a pending/requested review. Approving...")
|
||||
staging_bot_client.create_review("myproducts/mySLFO", project_pr_number, event="APPROVED", body="Staging bot approves")
|
||||
|
||||
# Check if all expected reviewers have at least one review record (any state)
|
||||
pkg_reviews = gitea_env.list_reviews("mypool/pkgA", package_pr1_number)
|
||||
current_reviewers = {r["user"]["login"] for r in pkg_reviews}
|
||||
|
||||
pkg_reviews = gitea_env.list_reviews("mypool/pkgB", package_pr2_number)
|
||||
current_reviewers = {r["user"]["login"] for r in pkg_reviews}
|
||||
|
||||
if expected_reviewers.issubset(current_reviewers):
|
||||
# Also ensure they are all approved (not just requested)
|
||||
approved_reviewers = {r["user"]["login"] for r in pkg_reviews if r["state"] == "APPROVED"}
|
||||
if expected_reviewers.issubset(approved_reviewers):
|
||||
# And check project PR for bot approval
|
||||
prj_approved = any(r["user"]["login"] == "autogits_obs_staging_bot" and r["state"] == "APPROVED" for r in prj_reviews)
|
||||
if prj_approved:
|
||||
all_requested = True
|
||||
print(f"All expected reviewers {expected_reviewers} and staging bot have approved.")
|
||||
break
|
||||
|
||||
pkg1_details = gitea_env.get_pr_details("mypool/pkgA", package_pr1_number)
|
||||
pkg2_details = gitea_env.get_pr_details("mypool/pkgB", package_pr2_number)
|
||||
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
assert not pkg1_details.get("merged"), "Package 1 PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
assert not pkg2_details.get("merged"), "Package 2 PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
assert not prj_details.get("merged"), "Project PR merged prematurely (ManualMergeOnly ignored?)"
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
assert all_requested, f"Timed out waiting for all expected reviewers {expected_reviewers} to approve. Current: {current_reviewers}"
|
||||
print("All PRs have all required approvals but are not merged (as expected with ManualMergeOnly).")
|
||||
|
||||
# 4. Comment "merge ok" from a requested reviewer (usera)
|
||||
print("Commenting 'merge ok' on package PR as user usera ...")
|
||||
usera_client.create_issue_comment("mypool/pkgA", package_pr1_number, "merge ok")
|
||||
ownerB_client.create_issue_comment("mypool/pkgB", package_pr2_number, "merge ok")
|
||||
|
||||
|
||||
# 5. Verify both PRs are merged
|
||||
print("Polling for PR merge status...")
|
||||
package1_merged = False
|
||||
package2_merged = False
|
||||
project_merged = False
|
||||
|
||||
for i in range(20): # Poll for up to 20 seconds
|
||||
if not package1_merged:
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr1_number)
|
||||
if pkg_details.get("merged"):
|
||||
package1_merged = True
|
||||
print(f"Package PR mypool/pkgA#{package_pr1_number} merged.")
|
||||
|
||||
if not package2_merged:
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgB", package_pr2_number)
|
||||
if pkg_details.get("merged"):
|
||||
package2_merged = True
|
||||
print(f"Package PR mypool/pkgB#{package_pr2_number} merged.")
|
||||
|
||||
if not project_merged:
|
||||
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
|
||||
if prj_details.get("merged"):
|
||||
project_merged = True
|
||||
print(f"Project PR myproducts/mySLFO#{project_pr_number} merged.")
|
||||
|
||||
if package1_merged and package2_merged and project_merged:
|
||||
break
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
assert not package1_merged, f"Package PR mypool/pkgA#{package_pr1_number} was merged after 'merge ok'."
|
||||
assert not package2_merged, f"Package PR mypool/pkgB#{package_pr2_number} was merged after 'merge ok'."
|
||||
assert not project_merged, f"Project PR myproducts/mySLFO#{project_pr_number} was merged after 'merge ok'."
|
||||
print("Both PRs merged not after 'merge ok'.")
|
||||
|
||||
@pytest.mark.t008
|
||||
def test_008_merge_mode_ff_only_success(merge_ff_env, test_user_client):
|
||||
"""
|
||||
@@ -151,7 +381,7 @@ index 0000000..e69de29
|
||||
"""
|
||||
package_pr = test_user_client.create_gitea_pr("mypool/pkgA", diff, "Test FF Merge", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
|
||||
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgA", package_pr_number)
|
||||
assert project_pr_number is not None
|
||||
|
||||
@@ -179,7 +409,7 @@ index 0000000..e69de29
|
||||
"""
|
||||
package_pr = ownerA_client.create_gitea_pr("mypool/pkgA", diff, "Test FF Merge Failure (Conflict)", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
|
||||
|
||||
# 2. Wait for project PR to be created
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgA", package_pr_number)
|
||||
assert project_pr_number is not None
|
||||
@@ -192,24 +422,24 @@ index 0000000..e69de29
|
||||
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
|
||||
|
||||
print("Pushing another change to PR branch to trigger sync...")
|
||||
gitea_env.modify_gitea_pr("mypool/pkgA", package_pr_number,
|
||||
gitea_env.modify_gitea_pr("mypool/pkgA", package_pr_number,
|
||||
"diff --git a/sync_test.txt b/sync_test.txt\nnew file mode 100644\nindex 0000000..e69de29\n",
|
||||
"Trigger Sync")
|
||||
|
||||
# The bot should detect it's not FF and NOT merge, and re-request reviews because of the new commit
|
||||
print("Waiting for reviews to be re-requested and approving again...")
|
||||
time.sleep(10) # Wait for bot to process sync
|
||||
|
||||
|
||||
# Approve again and verify it is NOT merged
|
||||
print("Approving again and verifying PR is NOT merged (because it's not FF)...")
|
||||
for i in range(15):
|
||||
gitea_env.approve_requested_reviews("mypool/pkgA", package_pr_number)
|
||||
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
|
||||
assert not pkg_details.get("merged"), "Package PR merged despite NOT being FF-mergeable!"
|
||||
|
||||
|
||||
print("FF-only failure (not merged after sync) verified.")
|
||||
|
||||
@pytest.mark.t010
|
||||
@@ -233,7 +463,7 @@ index 0000000..e69de29
|
||||
"""
|
||||
package_pr = ownerA_client.create_gitea_pr("mypool/pkgA", diff, "Test Devel Merge (Conflict)", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
|
||||
|
||||
# 2. Create a content conflict by committing the same file to the base branch
|
||||
gitea_env.create_file("mypool", "pkgA", filename, "Conflicting base content\n", branch=merge_branch_name)
|
||||
|
||||
@@ -275,10 +505,10 @@ index 0000000..e69de29
|
||||
"""
|
||||
package_pr = ownerA_client.create_gitea_pr("mypool/pkgA", diff, "Test Replace Merge (Conflict)", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
|
||||
|
||||
# Enable "Allow edits from maintainers"
|
||||
ownerA_client.update_gitea_pr_properties("mypool/pkgA", package_pr_number, allow_maintainer_edit=True)
|
||||
|
||||
|
||||
# 2. Create a content conflict by committing the same file to the base branch
|
||||
gitea_env.create_file("mypool", "pkgA", filename, "Conflicting base content\n", branch=merge_branch_name)
|
||||
|
||||
@@ -298,7 +528,7 @@ index 0000000..e69de29
|
||||
# Verify that the project branch HEAD is a merge commit
|
||||
branch_info = gitea_env._request("GET", f"repos/myproducts/mySLFO/branches/{merge_branch_name}").json()
|
||||
new_head_sha = branch_info["commit"]["id"]
|
||||
|
||||
|
||||
commit_details = gitea_env._request("GET", f"repos/myproducts/mySLFO/git/commits/{new_head_sha}").json()
|
||||
assert len(commit_details["parents"]) > 1, f"Project branch {merge_branch_name} HEAD should be a merge commit but has {len(commit_details['parents'])} parents"
|
||||
|
||||
@@ -327,7 +557,7 @@ index 0000000..e69de29
|
||||
"""
|
||||
package_pr = ownerA_client.create_gitea_pr("mypool/pkgA", diff, "Test Devel FF Merge", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
|
||||
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgA", package_pr_number)
|
||||
assert project_pr_number is not None
|
||||
|
||||
@@ -342,7 +572,7 @@ index 0000000..e69de29
|
||||
branch_info = gitea_env._request("GET", f"repos/mypool/pkgA/branches/{merge_branch_name}").json()
|
||||
new_head_sha = branch_info["commit"]["id"]
|
||||
assert new_head_sha == pkg_head_sha, f"Package branch {merge_branch_name} HEAD should be {pkg_head_sha} but is {new_head_sha}"
|
||||
|
||||
|
||||
commit_details = gitea_env._request("GET", f"repos/mypool/pkgA/git/commits/{new_head_sha}").json()
|
||||
assert len(commit_details["parents"]) == 1, f"Package branch {merge_branch_name} HEAD should have 1 parent but has {len(commit_details['parents'])}"
|
||||
|
||||
@@ -367,7 +597,7 @@ index 0000000..e69de29
|
||||
"""
|
||||
package_pr = ownerA_client.create_gitea_pr("mypool/pkgA", diff, "Test Replace FF Merge", False, base_branch=merge_branch_name)
|
||||
package_pr_number = package_pr["number"]
|
||||
|
||||
|
||||
project_pr_number = gitea_env.wait_for_project_pr("mypool/pkgA", package_pr_number)
|
||||
assert project_pr_number is not None
|
||||
|
||||
@@ -382,7 +612,7 @@ index 0000000..e69de29
|
||||
branch_info = gitea_env._request("GET", f"repos/mypool/pkgA/branches/{merge_branch_name}").json()
|
||||
new_head_sha = branch_info["commit"]["id"]
|
||||
assert new_head_sha == pkg_head_sha, f"Package branch {merge_branch_name} HEAD should be {pkg_head_sha} but is {new_head_sha}"
|
||||
|
||||
|
||||
commit_details = gitea_env._request("GET", f"repos/mypool/pkgA/git/commits/{new_head_sha}").json()
|
||||
assert len(commit_details["parents"]) == 1, f"Package branch {merge_branch_name} HEAD should have 1 parent but has {len(commit_details['parents'])}"
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"myproducts/mySLFO#review-required",
|
||||
"myproducts/mySLFO#label-test",
|
||||
"myproducts/mySLFO#manual-merge",
|
||||
"myproducts/mySLFO#manual-multipkg-merge",
|
||||
"myproducts/mySLFO#merge-ff",
|
||||
"myproducts/mySLFO#merge-replace",
|
||||
"myproducts/mySLFO#merge-devel"
|
||||
|
||||
@@ -19,7 +19,6 @@ package main
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
@@ -124,9 +123,7 @@ func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, co
|
||||
common.LogError(" - ", action.Repository.Name, "repo is not sha256. Ignoring.")
|
||||
return
|
||||
}
|
||||
relpath, err := common.RelativeRepositoryPath(gitOrg, action.Repository.Clone_Url)
|
||||
common.PanicOnError(err)
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", relpath, action.Repository.Name))
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name))
|
||||
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
||||
|
||||
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current"))
|
||||
@@ -271,12 +268,6 @@ func verifyProjectState(git common.Git, org string, config *common.AutogitConfig
|
||||
sub, err := git.GitSubmoduleList(gitPrj, "HEAD")
|
||||
common.PanicOnError(err)
|
||||
|
||||
submodulesData, err := git.GitCatFile(gitPrj, "HEAD", ".gitmodules")
|
||||
var submoduleEntries []common.Submodule
|
||||
if err == nil {
|
||||
submoduleEntries, _ = common.ParseSubmodulesFile(bytes.NewReader(submodulesData))
|
||||
}
|
||||
|
||||
common.LogDebug(" * Getting package links")
|
||||
var pkgLinks []*PackageRebaseLink
|
||||
if f, err := fs.Stat(os.DirFS(path.Join(git.GetPath(), gitPrj)), common.PrjLinksFile); err == nil && (f.Mode()&fs.ModeType == 0) && f.Size() < 1000000 {
|
||||
@@ -404,27 +395,8 @@ next_repo:
|
||||
}
|
||||
// }
|
||||
|
||||
relpath, err := common.RelativeRepositoryPath(gitOrg, r.CloneURL)
|
||||
common.PanicOnError(err)
|
||||
|
||||
for repo := range sub {
|
||||
if repo == r.Name {
|
||||
// verify we are using relative repository paths, and if not, adjust them
|
||||
sidx := slices.IndexFunc(submoduleEntries, func(s common.Submodule) bool {
|
||||
if path.Base(s.Path) == r.Name {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if sidx >= 0 && submoduleEntries[sidx].Url != relpath {
|
||||
submoduleEntries[sidx].Url = relpath
|
||||
f, err := os.OpenFile(path.Join(git.GetPath(), ".gitmodules"), os.O_CREATE|os.O_TRUNC, 0o6400)
|
||||
common.PanicOnError(err)
|
||||
defer f.Close()
|
||||
common.PanicOnError(common.WriteSubmodules(submoduleEntries, f))
|
||||
isGitUpdated = true
|
||||
}
|
||||
|
||||
// not missing
|
||||
continue next_repo
|
||||
}
|
||||
@@ -448,7 +420,7 @@ next_repo:
|
||||
}
|
||||
|
||||
// add repository to git project
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", relpath, r.Name))
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", r.CloneURL, r.Name))
|
||||
|
||||
curBranch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, r.Name), "branch", "--show-current"))
|
||||
if branch != curBranch {
|
||||
|
||||
@@ -54,6 +54,7 @@ This is the ProjectGit config file. For runtime config file, see bottom.
|
||||
| *GitProjectName* | Repository and branch where the ProjectGit lives. | no | string | **Format**: `org/project_repo#branch` | By default assumes `_ObsPrj` with default branch in the *Organization* |
|
||||
| *ManualMergeOnly* | Merges are permitted only upon receiving a "merge ok" comment from designated maintainers in the PkgGit PR. | no | bool | true, false | false |
|
||||
| *ManualMergeProject* | Merges are permitted only upon receiving a "merge ok" comment in the ProjectGit PR from project maintainers. | no | bool | true, false | false |
|
||||
| *MergeMode* | Type of package merge accepted. See below for details. | no | string | ff-only, replace, devel | ff-only |
|
||||
| *ReviewRequired* | If submitter is a maintainer, require review from another maintainer if available. | no | bool | true, false | false |
|
||||
| *NoProjectGitPR* | Do not create PrjGit PR, but still perform other tasks. | no | bool | true, false | false |
|
||||
| *Reviewers* | PrjGit reviewers. Additional review requests are triggered for associated PkgGit PRs. PrjGit PR is merged only when all reviews are complete. | no | array of strings | | `[]` |
|
||||
@@ -117,8 +118,6 @@ The following labels are used, when defined in Repo/Org.
|
||||
| Label Config Entry | Default label | Description
|
||||
|--------------------|----------------|----------------------------------------
|
||||
| StagingAuto | staging/Auto | Assigned to Project Git PRs when first staged
|
||||
| ReviewPending | review/Pending | Assigned to Project Git PR when package reviews are still pending
|
||||
| ReviewDone | review/Done | Assigned to Project Git PR when reviews are complete on all package PRs
|
||||
|
||||
|
||||
Maintainership
|
||||
|
||||
@@ -144,7 +144,6 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
|
||||
return err
|
||||
}
|
||||
|
||||
PrjGitOrg, _, _ := prset.Config.GetPrjGit()
|
||||
for _, pr := range prset.PRs {
|
||||
if prset.IsPrjGitPR(pr.PR) {
|
||||
continue
|
||||
@@ -199,12 +198,7 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
|
||||
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
|
||||
commitMsg := fmt.Sprintln("Add package", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nreferencing PRs:\n", ref)
|
||||
|
||||
relpath, err := common.RelativeRepositoryPath(PrjGitOrg, pr.PR.Base.Repo.CloneURL)
|
||||
if err != nil {
|
||||
common.LogError("Cannot calculate relative path for repository", pr.PR.Base.Repo.CloneURL, err)
|
||||
return err
|
||||
}
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, relpath, repo)
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, pr.PR.Base.Repo.SSHURL, repo)
|
||||
|
||||
updateSubmoduleInPR(repo, prHead, git)
|
||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
||||
@@ -488,11 +482,11 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
||||
if _, ok := err.(*repository.RepoMergePullRequestConflict); !ok {
|
||||
common.PanicOnError(err)
|
||||
}
|
||||
// } else {
|
||||
// Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.")
|
||||
// Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
|
||||
// State: "closed",
|
||||
// })
|
||||
// } else {
|
||||
// Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.")
|
||||
// Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
|
||||
// State: "closed",
|
||||
// })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,8 @@ func TestPrjGitDescription(t *testing.T) {
|
||||
Base: &models.PRBranchInfo{
|
||||
Ref: "main",
|
||||
Repo: &models.Repository{
|
||||
Name: "pkg-a",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
CloneURL: "http://example.com/test-org/pkg-a.git",
|
||||
SSHURL: "git@example.com:test-org/pkg-a.git",
|
||||
Name: "pkg-a",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -204,10 +202,8 @@ func TestSetSubmodulesToMatchPRSet(t *testing.T) {
|
||||
Base: &models.PRBranchInfo{
|
||||
Ref: "main",
|
||||
Repo: &models.Repository{
|
||||
Name: "pkg-a",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
CloneURL: "http://example.com/test-org/pkg-a.git",
|
||||
SSHURL: "git@example.com:test-org/pkg-a.git",
|
||||
Name: "pkg-a",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
@@ -634,12 +630,7 @@ func TestCreatePRjGitPR_Integration(t *testing.T) {
|
||||
PR: &models.PullRequest{
|
||||
State: "open",
|
||||
Base: &models.PRBranchInfo{
|
||||
Repo: &models.Repository{
|
||||
Name: "pkg-a",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
CloneURL: "http://example.com/test-org/pkg-a.git",
|
||||
SSHURL: "git@example.com:test-org/pkg-a.git",
|
||||
},
|
||||
Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}},
|
||||
},
|
||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
||||
},
|
||||
@@ -662,23 +653,14 @@ func TestCreatePRjGitPR_Integration(t *testing.T) {
|
||||
Base: &models.PRBranchInfo{
|
||||
Name: "main",
|
||||
RepoID: 1,
|
||||
Repo: &models.Repository{
|
||||
Name: "test-prj",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
CloneURL: "http://example.com/test-org/test-prj.git",
|
||||
SSHURL: "git@example.com:test-org/test-prj.git",
|
||||
},
|
||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Sha: "prj-head-sha",
|
||||
},
|
||||
}
|
||||
|
||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
CloneURL: "http://example.com/test-org/test-prj.git",
|
||||
SSHURL: "git@example.com:test-org/test-prj.git",
|
||||
}, nil).AnyTimes()
|
||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
||||
// CreatePullRequestIfNotExist returns isNew=true
|
||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil, true).AnyTimes()
|
||||
// Expect SetLabels to be called for new PR
|
||||
|
||||
Reference in New Issue
Block a user