7 Commits
test5 ... main

Author SHA256 Message Date
18435a8820 pr: unit tests for remote PrjGit changes
All checks were successful
go-generate-check / go-generate-check (push) Successful in 26s
Integration tests / t (push) Successful in 5m8s
Added unit tests when remote projectgit changes are allowed or
not allowed.
2026-02-27 16:30:30 +01:00
5f646f4520 Disable temporary comment adding in case of lacking permissions 2026-02-27 16:28:21 +01:00
88aa8c32fd Merge commit 'refs/pull/149/head' of src.opensuse.org:git-workflow/autogits
Some checks failed
go-generate-check / go-generate-check (push) Successful in 11s
Integration tests / t (push) Failing after 7m32s
2026-02-27 15:51:34 +01:00
99d27a48ff Support remote source in pull requests
This requires write permission by maintainer there
2026-02-27 15:50:31 +01:00
Andrii Nikitin
7832ef90c0 t: rename Gitea objects to avoid accidental collision with real repos
All checks were successful
go-generate-check / go-generate-check (pull_request) Successful in 8s
Integration tests / t (pull_request) Successful in 4m38s
Also address potential race condition between requested reviews and sending approvals in TC-MERGE-002
2026-02-27 13:22:26 +01:00
Andrii Nikitin
4691747038 t: add manual merge test and improve robustness
All checks were successful
go-generate-check / go-generate-check (pull_request) Successful in 26s
Integration tests / t (pull_request) Successful in 4m35s
- Add TC-MERGE-002: new end-to-end test for ManualMergeOnly functionality.
- Implement global object tracking in conftest.py to prevent redundant setup.
- Update test-plan.md to reflect current test implementation and skip statuses.
- Improve review tests robustness by using unique filenames and better assertions.
- Configure SLFO staging-main and manual-merge branches for monitored tests.
- Move flaky NoProjectGitPR tests from xfail to skip.
2026-02-27 02:30:47 +01:00
af2ff0bdd2 common: check for old pending request reviews
Timeline events will contain Reviews and ReviewRequests and
ReviewDismissed events. We need to handle this at event parsing
time and not to punt this to the query functions later on.

If the last event is an actual review, we use this.
If no review, check if last event associated with the reviewer
is Dismissed or Requested Review but not if a dismissed Review
preceeds it.
2026-02-27 01:03:26 +01:00
18 changed files with 497 additions and 273 deletions

View File

@@ -288,7 +288,7 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error) {
id, err := e.GitExecWithOutput(gitDir, "show-ref", "--heads", "--hash", branchName)
if err != nil {
return "", fmt.Errorf("Can't find default branch: %s", branchName)
return "", fmt.Errorf("Can't find default branch: %s in %s", branchName, gitDir)
}
id = strings.TrimSpace(SplitLines(id)[0])
@@ -302,7 +302,7 @@ func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error
func (e *GitHandlerImpl) GitRemoteHead(gitDir, remote, branchName string) (string, error) {
id, err := e.GitExecWithOutput(gitDir, "show-ref", "--hash", "--verify", "refs/remotes/"+remote+"/"+branchName)
if err != nil {
return "", fmt.Errorf("Can't find default branch: %s", branchName)
return "", fmt.Errorf("Can't find default branch: %s in %s", branchName, gitDir)
}
return strings.TrimSpace(id), nil

View File

@@ -711,13 +711,15 @@ func (r *BuildResultList) BuildResultSummary() (success, finished bool) {
if !ok {
panic("Unknown result code: " + result.Code)
}
if r.isLastBuild && result.Code == "unknown" {
// it means the package has never build yet,
// but we don't know the reason
detail.Finished = true
if r.isLastBuild {
// we are always finished, since it is the last result
// also when there is "unknown" state, it just means it
// it was never done
finished = true
} else {
finished = finished && detail.Finished
}
finished = finished && detail.Finished
success = success && detail.Success
if !finished {

View File

@@ -2,7 +2,7 @@
<title>openSUSE Leap 16.0 based on SLFO</title>
<description>Leap 16.0 based on SLES 16.0 (specifically SLFO:1.2)</description>
<link project="openSUSE:Backports:SLE-16.0"/>
<scmsync>http://gitea-test:3000/products/SLFO#main</scmsync>
<scmsync>http://gitea-test:3000/myproducts/mySLFO#staging-main</scmsync>
<person userid="dimstar_suse" role="maintainer"/>
<person userid="lkocman-factory" role="maintainer"/>
<person userid="maxlin_factory" role="maintainer"/>

View File

@@ -59,8 +59,8 @@ The testing will be conducted in a dedicated test environment that mimics the pr
| **TC-SYNC-002** | P | **Update ProjectGit PR from PackageGit PR** | 1. Push a new commit to an existing PackageGit PR. | 1. The corresponding ProjectGit PR's head branch is updated with the new commit. | High |
| **TC-SYNC-003** | P | **WIP Flag Synchronization** | 1. Mark a PackageGit PR as "Work In Progress".<br>2. Remove the WIP flag from the PackageGit PR. | 1. The corresponding ProjectGit PR is also marked as "Work In Progress".<br>2. The WIP flag on the ProjectGit PR is removed. | Medium |
| **TC-SYNC-004** | - | **WIP Flag (multiple referenced package PRs)** | 1. Create a ProjectGit PR that references multiple PackageGit PRs.<br>2. Mark one of the PackageGit PRs as "Work In Progress".<br>3. Remove the "Work In Progress" flag from all PackageGit PRs. | 1. The ProjectGit PR is marked as "Work In Progress".<br>2. The "Work In Progress" flag is removed from the ProjectGit PR only after it has been removed from all associated PackageGit PRs. | Medium |
| **TC-SYNC-005** | x | **NoProjectGitPR = true, edits disabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR without "Allow edits from maintainers" enabled. <br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The bot adds a warning comment to the PackageGit PR explaining that it cannot update the PR. | High |
| **TC-SYNC-006** | x | **NoProjectGitPR = true, edits enabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR with "Allow edits from maintainers" enabled.<br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The submodule commit on the project PR is updated with the new commit from the PackageGit PR. | High |
| **TC-SYNC-005** | S | **NoProjectGitPR = true, edits disabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR without "Allow edits from maintainers" enabled. <br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The bot adds a warning comment to the PackageGit PR explaining that it cannot update the PR. | High |
| **TC-SYNC-006** | S | **NoProjectGitPR = true, edits enabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR with "Allow edits from maintainers" enabled.<br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The submodule commit on the project PR is updated with the new commit from the PackageGit PR. | High |
| **TC-COMMENT-001** | - | **Detect duplicate comments** | 1. Create a PackageGit PR.<br>2. Wait for the `workflow-pr` bot to act on the PR.<br>3. Edit the body of the PR to trigger the bot a second time. | 1. The bot should not post a duplicate comment. | High |
| **TC-REVIEW-001** | P | **Add mandatory reviewers** | 1. Create a new PackageGit PR. | 1. All mandatory reviewers are added to both the PackageGit and ProjectGit PRs. | High |
| **TC-REVIEW-002** | - | **Add advisory reviewers** | 1. Create a new PackageGit PR with advisory reviewers defined in the configuration. | 1. Advisory reviewers are added to the PR, but their approval is not required for merging. | Medium |
@@ -70,7 +70,7 @@ The testing will be conducted in a dedicated test environment that mimics the pr
| **TC-REVIEW-006** | P | **Package PR created by an external user (reject)** | 1. Create a PackageGit PR from the account of a user who is not a package maintainer.<br>2. One of the package maintainers rejects the PR. | 1. All package maintainers are added as reviewers.<br>2. Once one maintainer rejects the PR, the other maintainers are removed as reviewers. | High |
| **TC-REVIEW-007** | P | **Package PR created by a maintainer with ReviewRequired=true** | 1. Set `ReviewRequired = true` in `workflow.config`.<br>2. Create a PackageGit PR from the account of a package maintainer. | 1. A review is requested from other package maintainers if available. | High |
| **TC-MERGE-001** | P | **Automatic Merge** | 1. Create a PackageGit PR.<br>2. Ensure all mandatory reviews are completed on both project and package PRs. | 1. The PR is automatically merged. | High |
| **TC-MERGE-002** | - | **ManualMergeOnly with Package Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a package maintainer for that package. | 1. The PR is merged. | High |
| **TC-MERGE-002** | P | **ManualMergeOnly with Package Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a package maintainer (or requested reviewer). | 1. The PR is merged. | High |
| **TC-MERGE-003** | - | **ManualMergeOnly with unauthorized user** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a user who is not a maintainer for that package. | 1. The PR is not merged. | High |
| **TC-MERGE-004** | - | **ManualMergeOnly with multiple packages** | 1. Create a ProjectGit PR that references multiple PackageGit PRs with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on each package PR from the account of a package maintainer. | 1. The PR is merged only after "merge ok" is commented on all associated PackageGit PRs. | High |
| **TC-MERGE-005** | - | **ManualMergeOnly with Project Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a project maintainer. | 1. The PR is merged. | High |
@@ -84,6 +84,7 @@ The testing will be conducted in a dedicated test environment that mimics the pr
#### Legend:
* P = implemented and passing;
* S = skipped because is not implemented yet to save some execution time;
* x = likely implemented, but investigation is needed;
* X = implemented and likely to pass, but someteimes may fail, but troubleshooting is needed;
* - = test is not implemented

View File

@@ -13,9 +13,9 @@ from tests.lib.common_test_utils import GiteaAPIClient
BRANCH_CONFIG_COMMON = {
"workflow.config": {
"Workflows": ["pr"],
"Organization": "pool",
"Organization": "mypool",
"Reviewers": ["-autogits_obs_staging_bot"],
"GitProjectName": "products/SLFO#{branch}"
"GitProjectName": "myproducts/mySLFO#{branch}"
},
"_maintainership.json": {
"": ["ownerX", "ownerY"],
@@ -25,7 +25,8 @@ BRANCH_CONFIG_COMMON = {
}
BRANCH_CONFIG_CUSTOM = {
"main": {
"main": {},
"staging-main": {
"workflow.config": {
"ManualMergeProject": True
},
@@ -54,6 +55,12 @@ BRANCH_CONFIG_CUSTOM = {
"NoProjectGitPR": True
}
},
"manual-merge": {
"workflow.config": {
"ManualMergeOnly": True,
"Reviewers": ["+usera", "+userb", "-autogits_obs_staging_bot"]
}
},
"label-test": {
"workflow.config": {
"ManualMergeProject": True,
@@ -67,6 +74,13 @@ BRANCH_CONFIG_CUSTOM = {
}
}
# Global state to track created Gitea objects during a pytest run
_CREATED_ORGS = set()
_CREATED_REPOS = set()
_CREATED_USERS = set()
_CREATED_LABELS = set()
_ADDED_COLLABORATORS = set() # format: (org_repo, username)
def setup_users_from_config(client: GiteaAPIClient, wf: dict, mt: dict):
"""
Parses workflow.config and _maintainership.json, creates users, and adds them as collaborators.
@@ -87,18 +101,27 @@ def setup_users_from_config(client: GiteaAPIClient, wf: dict, mt: dict):
# Create all users
for username in all_users:
client.create_user(username, "password123", f"{username}@example.com")
client.add_collaborator("products", "SLFO", username, "write")
if username not in _CREATED_USERS:
client.create_user(username, "password123", f"{username}@example.com")
_CREATED_USERS.add(username)
if ("myproducts/mySLFO", username) not in _ADDED_COLLABORATORS:
client.add_collaborator("myproducts", "mySLFO", username, "write")
_ADDED_COLLABORATORS.add(("myproducts/mySLFO", username))
# Set specific repository permissions based on maintainership
for pkg, users in mt.items():
repo_name = pkg if pkg else None
for username in users:
if not repo_name:
client.add_collaborator("pool", "pkgA", username, "write")
client.add_collaborator("pool", "pkgB", username, "write")
for r in ["pkgA", "pkgB"]:
if (f"mypool/{r}", username) not in _ADDED_COLLABORATORS:
client.add_collaborator("mypool", r, username, "write")
_ADDED_COLLABORATORS.add((f"mypool/{r}", username))
else:
client.add_collaborator("pool", repo_name, username, "write")
if (f"mypool/{repo_name}", username) not in _ADDED_COLLABORATORS:
client.add_collaborator("mypool", repo_name, username, "write")
_ADDED_COLLABORATORS.add((f"mypool/{repo_name}", username))
def ensure_config_file(client: GiteaAPIClient, owner: str, repo: str, branch: str, file_name: str, expected_content_dict: dict):
"""
@@ -149,33 +172,41 @@ def gitea_env():
raise Exception("Gitea not available.")
print("--- Starting Gitea Global Setup ---")
client.create_org("products")
client.create_org("pool")
client.create_repo("products", "SLFO")
client.create_repo("pool", "pkgA")
client.create_repo("pool", "pkgB")
client.update_repo_settings("products", "SLFO")
client.update_repo_settings("pool", "pkgA")
client.update_repo_settings("pool", "pkgB")
for org in ["myproducts", "mypool"]:
if org not in _CREATED_ORGS:
client.create_org(org)
_CREATED_ORGS.add(org)
for org, repo in [("myproducts", "mySLFO"), ("mypool", "pkgA"), ("mypool", "pkgB")]:
if f"{org}/{repo}" not in _CREATED_REPOS:
client.create_repo(org, repo)
client.update_repo_settings(org, repo)
_CREATED_REPOS.add(f"{org}/{repo}")
# Create labels
client.create_label("products", "SLFO", "staging/Backlog", color="#0000ff")
client.create_label("products", "SLFO", "review/Pending", color="#ffff00")
for name, color in [("staging/Backlog", "#0000ff"), ("review/Pending", "#ffff00")]:
if ("myproducts/mySLFO", name) not in _CREATED_LABELS:
client.create_label("myproducts", "mySLFO", name, color=color)
_CREATED_LABELS.add(("myproducts/mySLFO", name))
# Submodules in SLFO
client.add_submodules("products", "SLFO")
# Submodules in mySLFO
client.add_submodules("myproducts", "mySLFO")
client.add_collaborator("products", "SLFO", "autogits_obs_staging_bot", "write")
client.add_collaborator("products", "SLFO", "workflow-pr", "write")
client.add_collaborator("pool", "pkgA", "workflow-pr", "write")
client.add_collaborator("pool", "pkgB", "workflow-pr", "write")
for repo_full, bot in [("myproducts/mySLFO", "autogits_obs_staging_bot"),
("myproducts/mySLFO", "workflow-pr"),
("mypool/pkgA", "workflow-pr"),
("mypool/pkgB", "workflow-pr")]:
if (repo_full, bot) not in _ADDED_COLLABORATORS:
org_part, repo_part = repo_full.split("/")
client.add_collaborator(org_part, repo_part, bot, "write")
_ADDED_COLLABORATORS.add((repo_full, bot))
restart_needed = False
# Setup all branches and configs
for branch_name, custom_configs in BRANCH_CONFIG_CUSTOM.items():
# Ensure branch exists in all 3 repos
for owner, repo in [("products", "SLFO"), ("pool", "pkgA"), ("pool", "pkgB")]:
for owner, repo in [("myproducts", "mySLFO"), ("mypool", "pkgA"), ("mypool", "pkgB")]:
if branch_name != "main":
try:
main_sha = client._request("GET", f"repos/{owner}/{repo}/branches/main").json()["commit"]["id"]
@@ -201,9 +232,9 @@ def gitea_env():
else:
merged_configs[file_name] = custom_content
# Ensure config files in products/SLFO
# Ensure config files in myproducts/mySLFO
for file_name, content_dict in merged_configs.items():
if ensure_config_file(client, "products", "SLFO", branch_name, file_name, content_dict):
if ensure_config_file(client, "myproducts", "mySLFO", branch_name, file_name, content_dict):
restart_needed = True
# Setup users (using configs from this branch)
@@ -218,23 +249,35 @@ def gitea_env():
@pytest.fixture(scope="session")
def automerge_env(gitea_env):
return gitea_env, "products/SLFO", "merge"
return gitea_env, "myproducts/mySLFO", "merge"
@pytest.fixture(scope="session")
def staging_main_env(gitea_env):
return gitea_env, "myproducts/mySLFO", "staging-main"
@pytest.fixture(scope="session")
def manual_merge_env(gitea_env):
return gitea_env, "myproducts/mySLFO", "manual-merge"
@pytest.fixture(scope="session")
def maintainer_env(gitea_env):
return gitea_env, "products/SLFO", "maintainer-merge"
return gitea_env, "myproducts/mySLFO", "maintainer-merge"
@pytest.fixture(scope="session")
def review_required_env(gitea_env):
return gitea_env, "products/SLFO", "review-required"
return gitea_env, "myproducts/mySLFO", "review-required"
@pytest.fixture(scope="session")
def no_project_git_pr_env(gitea_env):
return gitea_env, "products/SLFO", "dev"
return gitea_env, "myproducts/mySLFO", "dev"
@pytest.fixture(scope="session")
def label_env(gitea_env):
return gitea_env, "products/SLFO", "label-test"
return gitea_env, "myproducts/mySLFO", "label-test"
@pytest.fixture(scope="session")
def usera_client(gitea_env):
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo="usera")
@pytest.fixture(scope="session")
def ownerA_client(gitea_env):
@@ -256,6 +299,6 @@ def staging_bot_client(gitea_env):
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("pool", "pkgA", username, "write")
gitea_env.add_collaborator("products", "SLFO", username, "write")
gitea_env.add_collaborator("mypool", "pkgA", 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)

View File

@@ -1,21 +1,21 @@
<resultlist state="0fef640bfb56c3e76fcfb698b19b59c0">
<result project="SUSE:SLFO:Main:PullRequest:1881" repository="standard" arch="aarch64" code="unpublished" state="unpublished">
<scmsync>https://src.suse.de/products/SLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scmsync>https://src.suse.de/myproducts/mySLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scminfo>d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scminfo>
<status package="openjpeg2" code="succeeded"/>
</result>
<result project="SUSE:SLFO:Main:PullRequest:1881" repository="standard" arch="ppc64le" code="unpublished" state="unpublished">
<scmsync>https://src.suse.de/products/SLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scmsync>https://src.suse.de/myproducts/mySLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scminfo>d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scminfo>
<status package="openjpeg2" code="succeeded"/>
</result>
<result project="SUSE:SLFO:Main:PullRequest:1881" repository="standard" arch="x86_64" code="unpublished" state="unpublished">
<scmsync>https://src.suse.de/products/SLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scmsync>https://src.suse.de/myproducts/mySLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scminfo>d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scminfo>
<status package="openjpeg2" code="succeeded"/>
</result>
<result project="SUSE:SLFO:Main:PullRequest:1881" repository="standard" arch="s390x" code="unpublished" state="unpublished">
<scmsync>https://src.suse.de/products/SLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scmsync>https://src.suse.de/myproducts/mySLFO.git?onlybuild=openjpeg2#d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scmsync>
<scminfo>d99ac14dedf9f44e1744c71aaf221d15f6bed479ca11f15738e98f3bf9ae05a1</scminfo>
<status package="openjpeg2" code="succeeded"/>
</result>

View File

@@ -2,7 +2,7 @@
<title>openSUSE Leap 16.0 based on SLFO</title>
<description>Leap 16.0 based on SLES 16.0 (specifically SLFO:1.2)</description>
<link project="openSUSE:Backports:SLE-16.0"/>
<scmsync>http://gitea-test:3000/products/SLFO#main</scmsync>
<scmsync>http://gitea-test:3000/myproducts/mySLFO#staging-main</scmsync>
<person userid="dimstar_suse" role="maintainer"/>
<person userid="lkocman-factory" role="maintainer"/>
<person userid="maxlin_factory" role="maintainer"/>
@@ -56,4 +56,4 @@
<arch>ppc64le</arch>
<arch>s390x</arch>
</repository>
</project>
</project>

View File

@@ -172,8 +172,8 @@ class GiteaAPIClient:
raise
# Get latest commit SHAs for the submodules
pkg_a_sha = self._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
pkg_b_sha = self._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
pkg_a_sha = self._request("GET", "repos/mypool/pkgA/branches/main").json()["commit"]["id"]
pkg_b_sha = self._request("GET", "repos/mypool/pkgB/branches/main").json()["commit"]["id"]
if not pkg_a_sha or not pkg_b_sha:
raise Exception("Error: Could not get submodule commit SHAs. Cannot apply patch.")
@@ -186,10 +186,10 @@ index 0000000..f1838bd
@@ -0,0 +1,6 @@
+[submodule "pkgA"]
+ path = pkgA
+ url = ../../pool/pkgA.git
+ url = ../../mypool/pkgA.git
+[submodule "pkgB"]
+ path = pkgB
+ url = ../../pool/pkgB.git
+ url = ../../mypool/pkgB.git
diff --git a/pkgA b/pkgA
new file mode 160000
index 0000000..{pkg_a_sha}
@@ -389,6 +389,14 @@ index 0000000..{pkg_b_sha}
response = self._request("PATCH", url, json=kwargs)
return response.json()
def create_issue_comment(self, repo_full_name: str, issue_number: int, body: str):
owner, repo = repo_full_name.split("/")
url = f"repos/{owner}/{repo}/issues/{issue_number}/comments"
data = {"body": body}
print(f"--- Creating comment on {repo_full_name} issue #{issue_number} ---")
response = self._request("POST", url, json=data)
return response.json()
def get_timeline_events(self, repo_full_name: str, pr_number: int):
owner, repo = repo_full_name.split("/")
url = f"repos/{owner}/{repo}/issues/{pr_number}/timeline"

View File

@@ -14,27 +14,28 @@ from tests.lib.common_test_utils import (
# =============================================================================
def test_pr_workflow_succeeded(gitea_env, mock_build_result):
def test_pr_workflow_succeeded(staging_main_env, mock_build_result):
"""End-to-end test for a successful PR workflow."""
gitea_env, test_full_repo_name, merge_branch_name = staging_main_env
diff = "diff --git a/test.txt b/test.txt\nnew file mode 100644\nindex 0000000..e69de29\n"
pr = gitea_env.create_gitea_pr("pool/pkgA", diff, "Test PR - should succeed", False)
pr = gitea_env.create_gitea_pr("mypool/pkgA", diff, "Test PR - should succeed", False, base_branch=merge_branch_name)
initial_pr_number = pr["number"]
compose_dir = Path(__file__).parent.parent
forwarded_pr_number = None
print(
f"Polling pool/pkgA PR #{initial_pr_number} timeline for forwarded PR event..."
f"Polling mypool/pkgA PR #{initial_pr_number} timeline for forwarded PR event..."
)
for _ in range(20):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", initial_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", initial_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
forwarded_pr_number = match.group(1)
break
@@ -43,13 +44,13 @@ def test_pr_workflow_succeeded(gitea_env, mock_build_result):
assert (
forwarded_pr_number is not None
), "Workflow bot did not create a pull_ref event on the timeline."
print(f"Found forwarded PR: products/SLFO #{forwarded_pr_number}")
print(f"Found forwarded PR: myproducts/mySLFO #{forwarded_pr_number}")
print(f"Polling products/SLFO PR #{forwarded_pr_number} for reviewer assignment...")
print(f"Polling myproducts/mySLFO PR #{forwarded_pr_number} for reviewer assignment...")
reviewer_added = False
for _ in range(15):
time.sleep(1)
pr_details = gitea_env.get_pr_details("products/SLFO", forwarded_pr_number)
pr_details = gitea_env.get_pr_details("myproducts/mySLFO", forwarded_pr_number)
if any(
r.get("login") == "autogits_obs_staging_bot"
for r in pr_details.get("requested_reviewers", [])
@@ -69,11 +70,11 @@ def test_pr_workflow_succeeded(gitea_env, mock_build_result):
capture_output=True,
)
print(f"Polling products/SLFO PR #{forwarded_pr_number} for final status...")
print(f"Polling myproducts/mySLFO PR #{forwarded_pr_number} for final status...")
status_comment_found = False
for _ in range(20):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("products/SLFO", forwarded_pr_number)
timeline_events = gitea_env.get_timeline_events("myproducts/mySLFO", forwarded_pr_number)
for event in timeline_events:
print(event.get("body", "not a body"))
if event.get("body") and "successful" in event["body"]:
@@ -84,27 +85,28 @@ def test_pr_workflow_succeeded(gitea_env, mock_build_result):
assert status_comment_found, "Staging bot did not post a 'successful' comment."
def test_pr_workflow_failed(gitea_env, mock_build_result):
def test_pr_workflow_failed(staging_main_env, mock_build_result):
"""End-to-end test for a failed PR workflow."""
gitea_env, test_full_repo_name, merge_branch_name = staging_main_env
diff = "diff --git a/another_test.txt b/another_test.txt\nnew file mode 100644\nindex 0000000..e69de29\n"
pr = gitea_env.create_gitea_pr("pool/pkgA", diff, "Test PR - should fail", False)
pr = gitea_env.create_gitea_pr("mypool/pkgA", diff, "Test PR - should fail", False, base_branch=merge_branch_name)
initial_pr_number = pr["number"]
compose_dir = Path(__file__).parent.parent
forwarded_pr_number = None
print(
f"Polling pool/pkgA PR #{initial_pr_number} timeline for forwarded PR event..."
f"Polling mypool/pkgA PR #{initial_pr_number} timeline for forwarded PR event..."
)
for _ in range(20):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", initial_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", initial_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
forwarded_pr_number = match.group(1)
break
@@ -113,13 +115,13 @@ def test_pr_workflow_failed(gitea_env, mock_build_result):
assert (
forwarded_pr_number is not None
), "Workflow bot did not create a pull_ref event on the timeline."
print(f"Found forwarded PR: products/SLFO #{forwarded_pr_number}")
print(f"Found forwarded PR: myproducts/mySLFO #{forwarded_pr_number}")
print(f"Polling products/SLFO PR #{forwarded_pr_number} for reviewer assignment...")
print(f"Polling myproducts/mySLFO PR #{forwarded_pr_number} for reviewer assignment...")
reviewer_added = False
for _ in range(15):
time.sleep(1)
pr_details = gitea_env.get_pr_details("products/SLFO", forwarded_pr_number)
pr_details = gitea_env.get_pr_details("myproducts/mySLFO", forwarded_pr_number)
if any(
r.get("login") == "autogits_obs_staging_bot"
for r in pr_details.get("requested_reviewers", [])
@@ -139,11 +141,11 @@ def test_pr_workflow_failed(gitea_env, mock_build_result):
capture_output=True,
)
print(f"Polling products/SLFO PR #{forwarded_pr_number} for final status...")
print(f"Polling myproducts/mySLFO PR #{forwarded_pr_number} for final status...")
status_comment_found = False
for _ in range(20):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("products/SLFO", forwarded_pr_number)
timeline_events = gitea_env.get_timeline_events("myproducts/mySLFO", forwarded_pr_number)
for event in timeline_events:
if event.get("body") and "failed" in event["body"]:
status_comment_found = True

View File

@@ -29,23 +29,23 @@ def test_001_project_pr_labels(label_env, staging_bot_client):
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgA on branch {branch_name} ---")
package_pr = gitea_env.create_gitea_pr("pool/pkgA", diff, "Test Labels Fixture", False, base_branch=branch_name)
print(f"--- Creating package PR in mypool/pkgA on branch {branch_name} ---")
package_pr = gitea_env.create_gitea_pr("mypool/pkgA", diff, "Test Labels Fixture", False, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgA#{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 = None
print(f"Polling pool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
print(f"Polling mypool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
@@ -53,16 +53,16 @@ index 0000000..e69de29
break
assert project_pr_number is not None, "Workflow bot did not create a project PR."
print(f"Found project PR: products/SLFO#{project_pr_number}")
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
# 3. Wait for the project PR to have the label "staging/Backlog"
print(f"Checking for 'staging/Backlog' label on project PR products/SLFO#{project_pr_number}...")
print(f"Checking for 'staging/Backlog' label on project PR myproducts/mySLFO#{project_pr_number}...")
backlog_label_found = False
expected_backlog_label = "staging/Backlog"
for _ in range(20):
project_pr_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
project_pr_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
labels = project_pr_details.get("labels", [])
label_names = [l["name"] for l in labels]
if expected_backlog_label in label_names:
@@ -70,21 +70,21 @@ index 0000000..e69de29
break
time.sleep(1)
assert backlog_label_found, f"Project PR products/SLFO#{project_pr_number} does not have the expected label '{expected_backlog_label}'."
print(f"Project PR products/SLFO#{project_pr_number} has the expected label '{expected_backlog_label}'.")
assert backlog_label_found, f"Project PR myproducts/mySLFO#{project_pr_number} does not have the expected label '{expected_backlog_label}'."
print(f"Project PR myproducts/mySLFO#{project_pr_number} has the expected label '{expected_backlog_label}'.")
# 4. Post approval from autogits_obs_staging_bot
print(f"--- Posting approval from autogits_obs_staging_bot on project PR products/SLFO#{project_pr_number} ---")
staging_bot_client.create_review("products/SLFO", project_pr_number, event="APPROVED", body="Staging OK")
print(f"--- Posting approval from autogits_obs_staging_bot on project PR myproducts/mySLFO#{project_pr_number} ---")
staging_bot_client.create_review("myproducts/mySLFO", project_pr_number, event="APPROVED", body="Staging OK")
# 5. Check that the project PR has the label "review/Pending"
print(f"Checking for 'review/Pending' label on project PR products/SLFO#{project_pr_number}...")
print(f"Checking for 'review/Pending' label on project PR myproducts/mySLFO#{project_pr_number}...")
pending_label_found = False
expected_pending_label = "review/Pending"
for _ in range(20):
project_pr_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
project_pr_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
labels = project_pr_details.get("labels", [])
label_names = [l["name"] for l in labels]
print(f"Current labels: {label_names}")
@@ -93,5 +93,5 @@ index 0000000..e69de29
break
time.sleep(1)
assert pending_label_found, f"Project PR products/SLFO#{project_pr_number} does not have the expected label '{expected_pending_label}'."
print(f"Project PR products/SLFO#{project_pr_number} has the expected label '{expected_pending_label}'.")
assert pending_label_found, f"Project PR myproducts/mySLFO#{project_pr_number} does not have the expected label '{expected_pending_label}'."
print(f"Project PR myproducts/mySLFO#{project_pr_number} has the expected label '{expected_pending_label}'.")

View File

@@ -7,37 +7,35 @@ from tests.lib.common_test_utils import GiteaAPIClient
@pytest.mark.t001
def test_001_automerge(automerge_env, test_user_client):
"""
Test scenario:
1. Setup custom workflow.config with mandatory reviewers (+usera, +userb).
2. Create a package PR in 'merge' branch.
3. Make sure the workflow-pr service created related project PR in 'merge' branch.
4. React on 'requested' reviews by approving them.
5. Make sure both PRs are merged automatically by the workflow-pr service.
Test scenario TC-MERGE-001:
1. Create a PackageGit PR.
2. Ensure all mandatory reviews are completed on both project and package PRs.
3. Verify the PR is merged automatically.
"""
gitea_env, test_full_repo_name, merge_branch_name = automerge_env
# 1. Create a package PR
diff = """diff --git a/merge_test_fixture.txt b/merge_test_fixture.txt
diff = """diff --git a/automerge_test.txt b/automerge_test.txt
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgA on branch {merge_branch_name} ---")
package_pr = test_user_client.create_gitea_pr("pool/pkgA", diff, "Test Automerge Fixture", False, base_branch=merge_branch_name)
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 Automerge Fixture", False, base_branch=merge_branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgA#{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 = None
print(f"Polling pool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
print(f"Polling mypool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
@@ -45,37 +43,153 @@ index 0000000..e69de29
break
assert project_pr_number is not None, "Workflow bot did not create a project PR."
print(f"Found project PR: products/SLFO#{project_pr_number}")
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
# 4. Make sure both PRs are merged automatically by the workflow-pr service
print("Polling for PR merge status and reacting on REQUEST_REVIEW...")
# 3. Approve reviews and verify merged
print("Approving reviews and verifying both PRs are merged...")
package_merged = False
project_merged = False
for i in range(15): # Poll for up to 15 seconds
# Package PR
for i in range(20): # Poll for up to 20 seconds
gitea_env.approve_requested_reviews("mypool/pkgA", package_pr_number)
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
if not package_merged:
pkg_details = gitea_env.get_pr_details("pool/pkgA", package_pr_number)
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
if pkg_details.get("merged"):
package_merged = True
print(f"Package PR pool/pkgA#{package_pr_number} merged.")
else:
gitea_env.approve_requested_reviews("pool/pkgA", package_pr_number)
print(f"Package PR mypool/pkgA#{package_pr_number} merged.")
# Project PR
if not project_merged:
prj_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
if prj_details.get("merged"):
project_merged = True
print(f"Project PR products/SLFO#{project_pr_number} merged.")
else:
gitea_env.approve_requested_reviews("products/SLFO", project_pr_number)
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 pool/pkgA#{package_pr_number} was not merged automatically."
assert project_merged, f"Project PR products/SLFO#{project_pr_number} was not merged automatically."
assert package_merged, f"Package PR mypool/pkgA#{package_pr_number} was not merged automatically."
assert project_merged, f"Project PR myproducts/mySLFO#{project_pr_number} was not merged automatically."
print("Both PRs merged successfully.")
@pytest.mark.t002
def test_002_manual_merge(manual_merge_env, test_user_client, usera_client, staging_bot_client):
"""
Test scenario TC-MERGE-002:
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 requested reviewer.
4. Verify the PR is 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 = None
print(f"Polling mypool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
if project_pr_number:
break
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 (usera)
print("Commenting 'merge ok' on package PR...")
usera_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 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'.")

View File

@@ -15,22 +15,24 @@ def test_001_review_requests_matching_config(automerge_env, ownerA_client):
"""
gitea_env, test_full_repo_name, branch_name = automerge_env
# 1. Create a package PR for pool/pkgB as ownerA
diff = """diff --git a/pkgB_test_001.txt b/pkgB_test_001.txt
# 1. Create a package PR for mypool/pkgB as ownerA
ts = int(time.time() * 1000)
filename = f"pkgB_test_{ts}.txt"
diff = f"""diff --git a/{filename} b/{filename}
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("pool/pkgB", diff, "Test Review Requests Config", True, base_branch=branch_name)
print(f"--- Creating package PR in mypool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("mypool/pkgB", diff, "Test Review Requests Config", True, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgB#{package_pr_number}")
print(f"Created package PR mypool/pkgB#{package_pr_number}")
# 2. Check that review requests came to ownerB, ownerBB, usera, and userb
print("Checking for review requests from maintainers and workflow.config...")
reviewers_requested = set()
expected_reviewers = {"ownerB", "ownerBB", "usera", "userb"}
for _ in range(30):
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if expected_reviewers.issubset(reviewers_requested):
break
@@ -61,9 +63,9 @@ def test_004_maintainer(maintainer_env, ownerA_client):
# 0.1 Verify all users from config exist
print("--- Verifying all users from config exist ---")
import json
wf_file = gitea_env.get_file_info("products", "SLFO", "workflow.config", branch=branch_name)
wf_file = gitea_env.get_file_info("myproducts", "mySLFO", "workflow.config", branch=branch_name)
wf = json.loads(base64.b64decode(wf_file["content"]).decode("utf-8"))
mt_file = gitea_env.get_file_info("products", "SLFO", "_maintainership.json", branch=branch_name)
mt_file = gitea_env.get_file_info("myproducts", "mySLFO", "_maintainership.json", branch=branch_name)
mt = json.loads(base64.b64decode(mt_file["content"]).decode("utf-8"))
expected_users = set()
@@ -80,27 +82,29 @@ def test_004_maintainer(maintainer_env, ownerA_client):
print(f"Verified user exists: {username}")
# 1. Create a package PR as ownerA
diff = """diff --git a/maintainer_test_fixture.txt b/maintainer_test_fixture.txt
ts = int(time.time() * 1000)
filename = f"maintainer_test_{ts}.txt"
diff = f"""diff --git a/{filename} b/{filename}
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgA on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("pool/pkgA", diff, "Test Maintainer Merge", True, base_branch=branch_name)
print(f"--- Creating package PR in mypool/pkgA on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("mypool/pkgA", diff, "Test Maintainer Merge", True, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgA#{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 = None
print(f"Polling pool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
print(f"Polling mypool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
@@ -108,7 +112,7 @@ index 0000000..e69de29
break
assert project_pr_number is not None, "Workflow bot did not create a project PR."
print(f"Found project PR: products/SLFO#{project_pr_number}")
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
# 3. Make sure both PRs are merged automatically WITHOUT manual approvals
print("Polling for PR merge status (only bot approval allowed)...")
@@ -118,39 +122,40 @@ index 0000000..e69de29
for i in range(15): # Poll for up to 15 seconds
# Package PR
if not package_merged:
pkg_details = gitea_env.get_pr_details("pool/pkgA", package_pr_number)
pkg_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
if pkg_details.get("merged"):
package_merged = True
print(f"Package PR pool/pkgA#{package_pr_number} merged.")
print(f"Package PR mypool/pkgA#{package_pr_number} merged.")
else:
# Approve ONLY bot if requested
reviews = gitea_env.list_reviews("pool/pkgA", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgA", package_pr_number)
if any(r["state"] == "REQUEST_REVIEW" and r["user"]["login"] == "autogits_obs_staging_bot" for r in reviews):
gitea_env.approve_requested_reviews("pool/pkgA", package_pr_number)
gitea_env.approve_requested_reviews("mypool/pkgA", package_pr_number)
# Project PR
if not project_merged:
prj_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
prj_details = gitea_env.get_pr_details("myproducts/mySLFO", project_pr_number)
if prj_details.get("merged"):
project_merged = True
print(f"Project PR products/SLFO#{project_pr_number} merged.")
print(f"Project PR myproducts/mySLFO#{project_pr_number} merged.")
else:
# Approve ONLY bot if requested
reviews = gitea_env.list_reviews("products/SLFO", project_pr_number)
reviews = gitea_env.list_reviews("myproducts/mySLFO", project_pr_number)
if any(r["state"] == "REQUEST_REVIEW" and r["user"]["login"] == "autogits_obs_staging_bot" for r in reviews):
gitea_env.approve_requested_reviews("products/SLFO", project_pr_number)
gitea_env.approve_requested_reviews("myproducts/mySLFO", project_pr_number)
if package_merged and project_merged:
break
time.sleep(1)
assert package_merged, f"Package PR pool/pkgA#{package_pr_number} was not merged automatically."
assert project_merged, f"Project PR products/SLFO#{project_pr_number} was not merged automatically."
assert package_merged, f"Package PR mypool/pkgA#{package_pr_number} was not merged automatically."
assert project_merged, f"Project PR myproducts/mySLFO#{project_pr_number} was not merged automatically."
print("Both PRs merged successfully by maintainer rule.")
@pytest.mark.t005
# @pytest.mark.xfail(reason="TBD troubleshoot")
def test_005_any_maintainer_approval_sufficient(maintainer_env, ownerA_client, ownerBB_client):
"""
Test scenario:
@@ -161,28 +166,30 @@ def test_005_any_maintainer_approval_sufficient(maintainer_env, ownerA_client, o
"""
gitea_env, test_full_repo_name, branch_name = maintainer_env
# 1. Create a package PR for pool/pkgB as ownerA
diff = """diff --git a/pkgB_test_fixture.txt b/pkgB_test_fixture.txt
# 1. Create a package PR for mypool/pkgB as ownerA
ts = int(time.time() * 1000)
filename = f"pkgB_test_{ts}.txt"
diff = f"""diff --git a/{filename} b/{filename}
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("pool/pkgB", diff, "Test Single Maintainer Merge", True, base_branch=branch_name)
print(f"--- Creating package PR in mypool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("mypool/pkgB", diff, "Test Single Maintainer Merge", True, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgB#{package_pr_number}")
print(f"Created package PR mypool/pkgB#{package_pr_number}")
# 2. Make sure the workflow-pr service created related project PR
project_pr_number = None
print(f"Polling pool/pkgB PR #{package_pr_number} timeline for forwarded PR event...")
print(f"Polling mypool/pkgB PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgB", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgB", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
@@ -190,13 +197,13 @@ index 0000000..e69de29
break
assert project_pr_number is not None, "Workflow bot did not create a project PR."
print(f"Found project PR: products/SLFO#{project_pr_number}")
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
# 3. Check that review requests came to ownerB and ownerBB
print("Checking for review requests from ownerB and ownerBB...")
reviewers_requested = set()
for _ in range(20):
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if "ownerB" in reviewers_requested and "ownerBB" in reviewers_requested:
break
@@ -208,41 +215,21 @@ index 0000000..e69de29
# 4. ownerBB leaves review, ownerB does not.
print("ownerBB approving the PR...")
ownerBB_client.create_review("pool/pkgB", package_pr_number, event="APPROVED", body="Approval from ownerBB")
ownerBB_client.create_review("mypool/pkgB", package_pr_number, event="APPROVED", body="Approval from ownerBB")
# 5. Check that both PRs are merged automatically
print("Polling for PR merge status (only bot approval allowed for project PR)...")
package_merged = False
project_merged = False
for i in range(15): # Poll for up to 15 seconds
# Package PR
if not package_merged:
pkg_details = gitea_env.get_pr_details("pool/pkgB", package_pr_number)
if pkg_details.get("merged"):
package_merged = True
print(f"Package PR pool/pkgB#{package_pr_number} merged.")
# Project PR
if not project_merged:
prj_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
if prj_details.get("merged"):
project_merged = True
print(f"Project PR products/SLFO#{project_pr_number} merged.")
else:
# Approve ONLY bot if requested
reviews = gitea_env.list_reviews("products/SLFO", project_pr_number)
if any(r["state"] == "REQUEST_REVIEW" and r["user"]["login"] == "autogits_obs_staging_bot" for r in reviews):
gitea_env.approve_requested_reviews("products/SLFO", project_pr_number)
if package_merged and project_merged:
break
# 5. Check that review request for ownerB is removed
print("Polling for ownerB review request removal...")
ownerB_removed = False
for _ in range(30):
time.sleep(1)
assert package_merged, f"Package PR pool/pkgB#{package_pr_number} was not merged automatically."
assert project_merged, f"Project PR products/SLFO#{project_pr_number} was not merged automatically."
print("Both PRs merged successfully with only one maintainer approval.")
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if "ownerB" not in reviewers_requested:
ownerB_removed = True
break
assert ownerB_removed, f"ownerB review request was not removed after ownerBB approval. Current requested: {reviewers_requested}"
print("Confirmed: ownerB review request removed after single maintainer approval.")
@pytest.mark.t006
@@ -257,37 +244,39 @@ def test_006_maintainer_rejection_removes_other_requests(maintainer_env, ownerA_
"""
gitea_env, test_full_repo_name, branch_name = maintainer_env
# 1. Create a package PR for pool/pkgB as ownerA
diff = """diff --git a/pkgB_rejection_test.txt b/pkgB_rejection_test.txt
# 1. Create a package PR for mypool/pkgB as ownerA
ts = int(time.time() * 1000)
filename = f"pkgB_rejection_test_{ts}.txt"
diff = f"""diff --git a/{filename} b/{filename}
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("pool/pkgB", diff, "Test Maintainer Rejection", True, base_branch=branch_name)
print(f"--- Creating package PR in mypool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("mypool/pkgB", diff, "Test Maintainer Rejection", True, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgB#{package_pr_number}")
print(f"Created package PR mypool/pkgB#{package_pr_number}")
# 2. Check that review requests came to ownerB and ownerBB
print("Checking for review requests from ownerB and ownerBB...")
for _ in range(20):
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if "ownerB" in reviewers_requested and "ownerBB" in reviewers_requested:
break
time.sleep(1)
else:
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
pytest.fail(f"ownerB and ownerBB were not both requested. Got: {reviewers_requested}")
# 3. ownerBB rejects the PR
print("ownerBB rejecting the PR...")
ownerBB_client.create_review("pool/pkgB", package_pr_number, event="REQUEST_CHANGES", body="Rejecting from ownerBB")
ownerBB_client.create_review("mypool/pkgB", package_pr_number, event="REQUEST_CHANGES", body="Rejecting from ownerBB")
# 4. Check that review request for ownerB is removed
print("Checking if ownerB's review request is removed...")
for _ in range(20):
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if "ownerB" not in reviewers_requested:
print("Confirmed: ownerB's review request was removed.")
@@ -315,28 +304,30 @@ def test_007_review_required_needs_all_approvals(review_required_env, ownerA_cli
ownerA_client._request("GET", "users/admin")
print(f"ownerA_client smoke test passed")
# 1. Create a package PR for pool/pkgB as ownerA
diff = """diff --git a/pkgB_review_required_test.txt b/pkgB_review_required_test.txt
# 1. Create a package PR for mypool/pkgB as ownerA
ts = int(time.time() * 1000)
filename = f"pkgB_review_required_test_{ts}.txt"
diff = f"""diff --git a/{filename} b/{filename}
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("pool/pkgB", diff, "Test Review Required", True, base_branch=branch_name)
print(f"--- Creating package PR in mypool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("mypool/pkgB", diff, "Test Review Required", True, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgB#{package_pr_number}")
print(f"Created package PR mypool/pkgB#{package_pr_number}")
# 2. Make sure the workflow-pr service created related project PR
project_pr_number = None
print(f"Polling pool/pkgB PR #{package_pr_number} timeline for forwarded PR event...")
print(f"Polling mypool/pkgB PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgB", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgB", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
@@ -344,38 +335,38 @@ index 0000000..e69de29
break
assert project_pr_number is not None, "Workflow bot did not create a project PR."
print(f"Found project PR: products/SLFO#{project_pr_number}")
print(f"Found project PR: myproducts/mySLFO#{project_pr_number}")
# 3. Check that review requests came to ownerB and ownerBB
print("Checking for review requests from ownerB and ownerBB...")
for _ in range(20):
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if "ownerB" in reviewers_requested and "ownerBB" in reviewers_requested:
break
time.sleep(1)
else:
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
pytest.fail(f"ownerB and ownerBB were not both requested. Got: {reviewers_requested}")
# 4. ownerBB leaves review, ownerB does not.
print("ownerBB approving the PR...")
ownerBB_client.create_review("pool/pkgB", package_pr_number, event="APPROVED", body="Approval from ownerBB")
ownerBB_client.create_review("mypool/pkgB", package_pr_number, event="APPROVED", body="Approval from ownerBB")
# 5. Check that the PR is NOT merged automatically and ownerB request remains
print("Waiting to ensure PR is NOT merged and ownerB request remains...")
for i in range(10):
pkg_details = gitea_env.get_pr_details("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
pkg_details = gitea_env.get_pr_details("mypool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
review_states = [(r["user"]["login"], r["state"]) for r in reviews]
print(f"Attempt {i+1}: Merged={pkg_details.get('merged')}, Reviews={review_states}")
time.sleep(2)
pkg_details = gitea_env.get_pr_details("pool/pkgB", package_pr_number)
pkg_details = gitea_env.get_pr_details("mypool/pkgB", package_pr_number)
assert not pkg_details.get("merged"), "Package PR was merged automatically but it should NOT have been (ReviewRequired=true)."
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviews = gitea_env.list_reviews("mypool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
assert "ownerB" in reviewers_requested, f"ownerB's review request was removed, but it should have remained. All reviews: {[(r['user']['login'], r['state']) for r in reviews]}"

View File

@@ -23,7 +23,7 @@ pytest.forwarded_pr_number = None
def test_001_project_pr(gitea_env):
"""Forwarded PR correct title"""
diff = "diff --git a/another_test.txt b/another_test.txt\nnew file mode 100644\nindex 0000000..e69de29\n"
pytest.pr = gitea_env.create_gitea_pr("pool/pkgA", diff, "Test PR", False)
pytest.pr = gitea_env.create_gitea_pr("mypool/pkgA", diff, "Test PR", False)
pytest.initial_pr_number = pytest.pr["number"]
time.sleep(5) # Give Gitea some time to process the PR and make the timeline available
@@ -31,18 +31,18 @@ def test_001_project_pr(gitea_env):
pytest.forwarded_pr_number = None
print(
f"Polling pool/pkgA PR #{pytest.initial_pr_number} timeline for forwarded PR event..."
f"Polling mypool/pkgA PR #{pytest.initial_pr_number} timeline for forwarded PR event..."
)
# Instead of polling timeline, check if forwarded PR exists directly
for _ in range(20):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", pytest.initial_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", pytest.initial_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
pytest.forwarded_pr_number = match.group(1)
break
@@ -51,7 +51,7 @@ def test_001_project_pr(gitea_env):
assert (
pytest.forwarded_pr_number is not None
), "Workflow bot did not create a forwarded PR."
pytest.pr_details = gitea_env.get_pr_details("products/SLFO", pytest.forwarded_pr_number)
pytest.pr_details = gitea_env.get_pr_details("myproducts/mySLFO", pytest.forwarded_pr_number)
assert (
pytest.pr_details["title"] == "Forwarded PRs: pkgA"
), "Forwarded PR correct title"
@@ -62,13 +62,13 @@ def test_001_project_pr(gitea_env):
def test_002_updated_project_pr(gitea_env):
"""Forwarded PR head is updated"""
diff = "diff --git a/another_test.txt b/another_test.txt\nnew file mode 100444\nindex 0000000..e69de21\n"
gitea_env.modify_gitea_pr("pool/pkgA", pytest.initial_pr_number, diff, "Tweaks")
gitea_env.modify_gitea_pr("mypool/pkgA", pytest.initial_pr_number, diff, "Tweaks")
sha_old = pytest.pr_details["head"]["sha"]
sha_changed = False
for _ in range(20):
time.sleep(1)
new_pr_details = gitea_env.get_pr_details("products/SLFO", pytest.forwarded_pr_number)
new_pr_details = gitea_env.get_pr_details("myproducts/mySLFO", pytest.forwarded_pr_number)
sha_new = new_pr_details["head"]["sha"]
if sha_new != sha_old:
print(f"Sha changed from {sha_old} to {sha_new}")
@@ -82,17 +82,17 @@ def test_002_updated_project_pr(gitea_env):
@pytest.mark.dependency(depends=["test_001_project_pr"])
def test_003_wip(gitea_env):
"""WIP flag set for PR"""
# 1. set WIP flag in PR f"pool/pkgA#{pytest.initial_pr_number}"
initial_pr_details = gitea_env.get_pr_details("pool/pkgA", pytest.initial_pr_number)
# 1. set WIP flag in PR f"mypool/pkgA#{pytest.initial_pr_number}"
initial_pr_details = gitea_env.get_pr_details("mypool/pkgA", pytest.initial_pr_number)
wip_title = "WIP: " + initial_pr_details["title"]
gitea_env.update_gitea_pr_properties("pool/pkgA", pytest.initial_pr_number, title=wip_title)
# 2. in loop check whether WIP flag is set for PR f"products/SLFO #{pytest.forwarded_pr_number}"
gitea_env.update_gitea_pr_properties("mypool/pkgA", pytest.initial_pr_number, title=wip_title)
# 2. in loop check whether WIP flag is set for PR f"myproducts/mySLFO #{pytest.forwarded_pr_number}"
wip_flag_set = False
for _ in range(20):
time.sleep(1)
forwarded_pr_details = gitea_env.get_pr_details(
"products/SLFO", pytest.forwarded_pr_number
"myproducts/mySLFO", pytest.forwarded_pr_number
)
if "WIP: " in forwarded_pr_details["title"]:
wip_flag_set = True
@@ -100,19 +100,19 @@ def test_003_wip(gitea_env):
assert wip_flag_set, "WIP flag was not set in the forwarded PR."
# Remove WIP flag from PR f"pool/pkgA#{pytest.initial_pr_number}"
initial_pr_details = gitea_env.get_pr_details("pool/pkgA", pytest.initial_pr_number)
# Remove WIP flag from PR f"mypool/pkgA#{pytest.initial_pr_number}"
initial_pr_details = gitea_env.get_pr_details("mypool/pkgA", pytest.initial_pr_number)
non_wip_title = initial_pr_details["title"].replace("WIP: ", "")
gitea_env.update_gitea_pr_properties(
"pool/pkgA", pytest.initial_pr_number, title=non_wip_title
"mypool/pkgA", pytest.initial_pr_number, title=non_wip_title
)
# In loop check whether WIP flag is removed for PR f"products/SLFO #{pytest.forwarded_pr_number}"
# In loop check whether WIP flag is removed for PR f"myproducts/mySLFO #{pytest.forwarded_pr_number}"
wip_flag_removed = False
for _ in range(20):
time.sleep(1)
forwarded_pr_details = gitea_env.get_pr_details(
"products/SLFO", pytest.forwarded_pr_number
"myproducts/mySLFO", pytest.forwarded_pr_number
)
if "WIP: " not in forwarded_pr_details["title"]:
wip_flag_removed = True
@@ -121,7 +121,7 @@ def test_003_wip(gitea_env):
@pytest.mark.t005
@pytest.mark.xfail(reason="works only in ibs_state branch?")
@pytest.mark.skip(reason="works only in ibs_state branch?")
@pytest.mark.dependency()
def test_005_NoProjectGitPR_edits_disabled(no_project_git_pr_env, test_user_client):
"""
@@ -139,7 +139,7 @@ index 0000000..e69de29
@@ -0,0 +1 @@
+Initial content
"""
package_pr = test_user_client.create_gitea_pr("pool/pkgA", initial_diff, "Test PR for No Project PR, No Edits", False, base_branch=dev_branch_name)
package_pr = test_user_client.create_gitea_pr("mypool/pkgA", initial_diff, "Test PR for No Project PR, No Edits", False, base_branch=dev_branch_name)
package_pr_number = package_pr["number"]
print(f"Created Package PR #{package_pr_number}")
@@ -147,29 +147,29 @@ index 0000000..e69de29
project_pr_created = False
for i in range(10): # Poll for some time
time.sleep(2)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_created = True
break
if project_pr_created:
break
assert not project_pr_created, "Workflow bot unexpectedly created a Project PR in products/SLFO."
assert not project_pr_created, "Workflow bot unexpectedly created a Project PR in myproducts/mySLFO."
print("Verification complete: No Project PR was created by the bot.")
# 3. Manually create the Project PR
pkgA_main_sha = gitea_env._request("GET", f"repos/pool/pkgA/branches/{dev_branch_name}").json()["commit"]["id"]
package_pr_details = gitea_env.get_pr_details("pool/pkgA", package_pr_number)
pkgA_main_sha = gitea_env._request("GET", f"repos/mypool/pkgA/branches/{dev_branch_name}").json()["commit"]["id"]
package_pr_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
pkgA_pr_head_sha = package_pr_details["head"]["sha"]
project_pr_title = "Forwarded PRs: pkgA (Manual)"
project_pr_body = f"Manual Project PR for NoProjectGitPR. \nPR: pool/pkgA!{package_pr_number}"
project_pr_body = f"Manual Project PR for NoProjectGitPR. \nPR: mypool/pkgA!{package_pr_number}"
project_pr_diff = f"""diff --git a/pkgA b/pkgA
index {pkgA_main_sha[:7]}..{pkgA_pr_head_sha[:7]} 160000
--- a/pkgA
@@ -199,14 +199,14 @@ index 0000000..e69de29
@@ -0,0 +1 @@
+Trigger content
"""
test_user_client.modify_gitea_pr("pool/pkgA", package_pr_number, new_diff_content, "Trigger bot update")
test_user_client.modify_gitea_pr("mypool/pkgA", package_pr_number, new_diff_content, "Trigger bot update")
# 5. Verify that the bot adds a warning comment because it cannot update the manual PR (edits disabled)
warning_found = False
print(f"Polling Package PR #{package_pr_number} for warning comment...")
for _ in range(20):
time.sleep(3)
comments = gitea_env.get_comments("pool/pkgA", package_pr_number)
comments = gitea_env.get_comments("mypool/pkgA", package_pr_number)
for comment in comments:
# According to test-plan.md, the warning explains that it cannot update the PR.
if "cannot update" in comment.get("body", "").lower():
@@ -221,7 +221,7 @@ index 0000000..e69de29
@pytest.mark.t006
@pytest.mark.xfail(reason="works only in ibs_state branch?")
@pytest.mark.skip(reason="works only in ibs_state branch?")
@pytest.mark.dependency()
def test_006_NoProjectGitPR_edits_enabled(no_project_git_pr_env, test_user_client):
"""
@@ -239,42 +239,42 @@ index 0000000..e69de29
@@ -0,0 +1 @@
+New feature content
"""
package_pr = test_user_client.create_gitea_pr("pool/pkgA", diff, "Test PR for NoProjectGitPR", False, base_branch=dev_branch_name)
package_pr = test_user_client.create_gitea_pr("mypool/pkgA", diff, "Test PR for NoProjectGitPR", False, base_branch=dev_branch_name)
package_pr_number = package_pr["number"]
# Enable "Allow edits from maintainers"
test_user_client.update_gitea_pr_properties("pool/pkgA", package_pr_number, allow_maintainer_edit=True)
test_user_client.update_gitea_pr_properties("mypool/pkgA", package_pr_number, allow_maintainer_edit=True)
print(f"Created Package PR #{package_pr_number} and enabled 'Allow edits from maintainers'.")
# Get SHAs needed for the manual Project PR diff
pkgA_main_sha = gitea_env._request("GET", f"repos/pool/pkgA/branches/{dev_branch_name}").json()["commit"]["id"]
package_pr_details = gitea_env.get_pr_details("pool/pkgA", package_pr_number)
pkgA_main_sha = gitea_env._request("GET", f"repos/mypool/pkgA/branches/{dev_branch_name}").json()["commit"]["id"]
package_pr_details = gitea_env.get_pr_details("mypool/pkgA", package_pr_number)
pkgA_pr_head_sha = package_pr_details["head"]["sha"]
# 3. Assert that the workflow-pr bot did not create a Project PR in the products/SLFO repository
# 3. Assert that the workflow-pr bot did not create a Project PR in the myproducts/mySLFO repository
project_pr_created = False
for i in range(20): # Poll for a reasonable time
time.sleep(2) # Wait a bit longer to be sure
timeline_events = gitea_env.get_timeline_events("pool/pkgA", package_pr_number)
timeline_events = gitea_env.get_timeline_events("mypool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
# Regex now searches for products/SLFO/pulls/(\d+)
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
# Regex now searches for myproducts/mySLFO/pulls/(\d+)
match = re.search(r"myproducts/mySLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_created = True
break
if project_pr_created:
break
assert not project_pr_created, "Workflow bot unexpectedly created a Project PR in products/SLFO."
print("Verification complete: No Project PR was created in products/SLFO as expected.")
assert not project_pr_created, "Workflow bot unexpectedly created a Project PR in myproducts/mySLFO."
print("Verification complete: No Project PR was created in myproducts/mySLFO as expected.")
# 1. Create that Project PR from the test code.
project_pr_title = "Forwarded PRs: pkgA"
project_pr_body = f"Test Project PR for NoProjectGitPR. \nPR: pool/pkgA!{package_pr_number}"
project_pr_body = f"Test Project PR for NoProjectGitPR. \nPR: mypool/pkgA!{package_pr_number}"
project_pr_diff = f"""diff --git a/pkgA b/pkgA
index {pkgA_main_sha[:7]}..{pkgA_pr_head_sha[:7]} 160000
--- a/pkgA
@@ -304,7 +304,7 @@ index 0000000..f587a12
@@ -0,0 +1 @@
+Another file content
"""
test_user_client.modify_gitea_pr("pool/pkgA", package_pr_number, new_diff_content, "Add another file to Package PR")
test_user_client.modify_gitea_pr("mypool/pkgA", package_pr_number, new_diff_content, "Add another file to Package PR")
print(f"Added new commit to Package PR #{package_pr_number}.")
time.sleep(5) # Give the bot time to react

View File

@@ -19,8 +19,8 @@ export GITEA_TOKEN
echo "GITEA_TOKEN exported (length: ${#GITEA_TOKEN})"
# Wait for the dummy data to be created by the gitea setup script
echo "Waiting for workflow.config in products/SLFO..."
API_URL="http://gitea-test:3000/api/v1/repos/products/SLFO/contents/workflow.config"
echo "Waiting for workflow.config in myproducts/mySLFO..."
API_URL="http://gitea-test:3000/api/v1/repos/myproducts/mySLFO/contents/workflow.config"
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_TOKEN" "$API_URL")
while [ "$HTTP_STATUS" != "200" ]; do

View File

@@ -1,8 +1,10 @@
[
"products/SLFO#main",
"products/SLFO#dev",
"products/SLFO#merge",
"products/SLFO#maintainer-merge",
"products/SLFO#review-required",
"products/SLFO#label-test"
"myproducts/mySLFO#main",
"myproducts/mySLFO#staging-main",
"myproducts/mySLFO#dev",
"myproducts/mySLFO#merge",
"myproducts/mySLFO#maintainer-merge",
"myproducts/mySLFO#review-required",
"myproducts/mySLFO#label-test",
"myproducts/mySLFO#manual-merge"
]

View File

@@ -1 +0,0 @@
4

View File

@@ -309,14 +309,17 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
PrjGit := PrjGitPR.PR.Base.Repo
prjGitPRbranch := PrjGitPR.PR.Head.Name
if PrjGitPR.PR.Base.RepoID != PrjGitPR.PR.Head.RepoID {
PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, "", PrjGit.SSHURL)
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitPR.PR.Head.Sha)
git.GitExecOrPanic(common.DefaultGitPrj, "checkout", PrjGitPR.PR.Head.Sha)
common.LogInfo("Cannot update this PR as it's on another remote, not branch:", prjGitPRbranch, "Assuming this is by-design. (eg. project git PR only)")
return nil
// permission check, if submission comes from foreign repo
if !PrjGitPR.PR.AllowMaintainerEdit {
// well, wrong place...
// common.LogError("Warning: source and target branch are in different repositories. We may not have the right permissions...")
// Gitea.AddComment(PrjGitPR.PR, "This PR does not allow maintainer changes, but referenced package branch has changed!")
return nil
}
}
PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, prjGitPRbranch, PrjGit.SSHURL)
// PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, prjGitPRbranch, PrjGit.SSHURL)
PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, PrjGitPR.PR.Head.Ref, PrjGitPR.PR.Head.Repo.SSHURL)
common.PanicOnError(err)
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitBranch)
@@ -364,6 +367,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
}
common.PanicOnError(git.GitExec(common.DefaultGitPrj, params...))
PrjGitPR.PR.Head.Sha = newHeadCommit
Gitea.SetLabels(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, []string{prset.Config.Label("PR/updated")})
}
// update PR

View File

@@ -396,7 +396,62 @@ func TestUpdatePrjGitPR(t *testing.T) {
Name: "feature",
RepoID: 2, // Different RepoID
Sha: "sha1",
Repo: &models.Repository{
SSHURL: "url",
},
},
User: &models.User{UserName: "someone"},
Mergeable: true,
AllowMaintainerEdit: true,
},
},
{
PR: &models.PullRequest{
State: "open",
Base: &models.PRBranchInfo{
Name: "other",
Repo: &models.Repository{
Name: "other-pkg",
Owner: &models.User{UserName: "test-org"},
},
},
Head: &models.PRBranchInfo{
Sha: "other-sha",
},
},
},
},
}
mockGit.EXPECT().GitClone(common.DefaultGitPrj, gomock.Any(), gomock.Any()).Return("remote2", nil)
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "fetch", "remote2", "main")
mockGit.EXPECT().GitBranchHead(common.DefaultGitPrj, gomock.Any()).Return("sha1", nil).Times(2)
mockGit.EXPECT().GitSubmoduleList(common.DefaultGitPrj, "HEAD").Return(map[string]string{"other-pkg": "other-sha"}, nil)
err := processor.UpdatePrjGitPR(prset)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("PR on another remote - not allowed", func(t *testing.T) {
prset := &common.PRSet{
Config: config,
PRs: []*common.PRInfo{
{
PR: &models.PullRequest{
Base: &models.PRBranchInfo{
Name: "main",
RepoID: 1,
Repo: &models.Repository{
Name: "test-prj",
Owner: &models.User{UserName: "test-org"},
},
},
Head: &models.PRBranchInfo{
Name: "feature",
RepoID: 2, // Different RepoID
},
AllowMaintainerEdit: false,
},
},
{
@@ -412,10 +467,6 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
},
}
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("remote2", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "remote2", "sha1")
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "sha1")
err := processor.UpdatePrjGitPR(prset)
if err != nil {
t.Errorf("Unexpected error: %v", err)
@@ -445,6 +496,9 @@ func TestUpdatePrjGitPR(t *testing.T) {
Name: "PR_branch",
RepoID: 1,
Sha: "old-head",
Repo: &models.Repository{
SSHURL: "url",
},
},
},
},
@@ -483,6 +537,7 @@ func TestUpdatePrjGitPR(t *testing.T) {
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
// UpdatePullRequest expectation
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
err := processor.UpdatePrjGitPR(prset)
@@ -514,6 +569,9 @@ func TestUpdatePrjGitPR(t *testing.T) {
Name: "PR_branch",
RepoID: 1,
Sha: "head",
Repo: &models.Repository{
SSHURL: "url",
},
},
},
},