From 7daeee9375502165e3b95fc6df3ffbf1f2eba403 Mon Sep 17 00:00:00 2001 From: Eli Uriegas Date: Mon, 2 Feb 2026 19:19:59 -0800 Subject: [PATCH 1/4] Update [ghstack-poisoned] --- src/ghstack/test_prelude.py | 46 +++++++++- test/land/dry_run.py.test | 49 +++++++++++ test/land/validate_rules_fail.py.test | 46 ++++++++++ test/land/validate_rules_pass.py.test | 47 ++++++++++ test/merge_rules/approval_validation.py.test | 93 ++++++++++++++++++++ test/merge_rules/check_validation.py.test | 91 +++++++++++++++++++ test/merge_rules/ignore_flaky.py.test | 61 +++++++++++++ test/merge_rules/pattern_matching.py.test | 82 +++++++++++++++++ test/merge_rules/yaml_parsing.py.test | 64 ++++++++++++++ 9 files changed, 578 insertions(+), 1 deletion(-) create mode 100644 test/land/dry_run.py.test create mode 100644 test/land/validate_rules_fail.py.test create mode 100644 test/land/validate_rules_pass.py.test create mode 100644 test/merge_rules/approval_validation.py.test create mode 100644 test/merge_rules/check_validation.py.test create mode 100644 test/merge_rules/ignore_flaky.py.test create mode 100644 test/merge_rules/pattern_matching.py.test create mode 100644 test/merge_rules/yaml_parsing.py.test diff --git a/src/ghstack/test_prelude.py b/src/ghstack/test_prelude.py index 567fadb..b3d7218 100644 --- a/src/ghstack/test_prelude.py +++ b/src/ghstack/test_prelude.py @@ -53,6 +53,9 @@ "get_github", "get_pr_reviewers", "get_pr_labels", + "set_pr_files", + "set_pr_reviews", + "set_pr_check_runs", "tick", "captured_output", ] @@ -225,7 +228,14 @@ def gh_submit( return r -def gh_land(pull_request: str) -> None: +def gh_land( + pull_request: str, + *, + validate_rules: bool = False, + dry_run: bool = False, + comment_on_failure: bool = False, + rules_file: Optional[str] = None, +) -> None: self = CTX return ghstack.land.main( remote_name="origin", @@ -233,6 +243,10 @@ def gh_land(pull_request: str) -> None: github=self.github, sh=self.sh, github_url="github.com", + validate_rules=validate_rules, + dry_run=dry_run, + comment_on_failure=comment_on_failure, + rules_file=rules_file, ) @@ -412,6 +426,36 @@ def get_pr_labels(pr_number: int) -> List[str]: return pr.labels +def set_pr_files(pr_number: int, files: List[str]) -> None: + """Set the list of changed files for a PR.""" + github = get_github() + repo = github.state.repository("pytorch", "pytorch") + pr = github.state.pull_request(repo, ghstack.github_fake.GitHubNumber(pr_number)) + pr.files = files + + +def set_pr_reviews(pr_number: int, reviews: List[Tuple[str, str]]) -> None: + """Set reviews for a PR. reviews is list of (user, state) tuples.""" + github = get_github() + repo = github.state.repository("pytorch", "pytorch") + pr = github.state.pull_request(repo, ghstack.github_fake.GitHubNumber(pr_number)) + pr.reviews = [ + ghstack.github_fake.PullRequestReview(user=u, state=s) for u, s in reviews + ] + + +def set_pr_check_runs( + pr_number: int, checks: List[Tuple[str, str, Optional[str]]] +) -> None: + """Set check runs for a PR. checks is list of (name, status, conclusion) tuples.""" + github = get_github() + repo = github.state.repository("pytorch", "pytorch") + pr = github.state.pull_request(repo, ghstack.github_fake.GitHubNumber(pr_number)) + pr.check_runs = [ + ghstack.github_fake.CheckRun(name=n, status=s, conclusion=c) for n, s, c in checks + ] + + def assert_eq(a: Any, b: Any) -> None: assert a == b, f"{a} != {b}" diff --git a/test/land/dry_run.py.test b/test/land/dry_run.py.test new file mode 100644 index 0000000..90a4395 --- /dev/null +++ b/test/land/dry_run.py.test @@ -0,0 +1,49 @@ +from ghstack.test_prelude import * + +import os +import tempfile +import ghstack.merge_rules +from ghstack.github_fake import GitHubNumber, PullRequestReview, CheckRun + +init_test() +commit("A") +(diff,) = gh_submit("Initial 1") +pr_url = diff.pr_url + +# Set up PR with files, approvals, and passing checks +github = get_github() +repo = github.state.repository("pytorch", "pytorch") +pr = github.state.pull_request(repo, GitHubNumber(500)) +pr.files = ["src/core/module.py"] +pr.reviews = [PullRequestReview(user="maintainer1", state="APPROVED")] +pr.check_runs = [CheckRun(name="CI", status="completed", conclusion="success")] + +# Create a temporary rules file +rules_content = """ +- name: core-changes + patterns: ["src/core/*"] + approved_by: ["maintainer1"] + mandatory_checks_name: ["CI"] +""" + +with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + f.write(rules_content) + rules_file = f.name + +try: + # Get the initial state of master + initial_log = get_upstream_sh().git("log", "--oneline", "master") + + # Dry run should NOT land the commit + gh_land(pr_url, validate_rules=True, dry_run=True, rules_file=rules_file) + + # Verify master is unchanged (no commit was landed) + final_log = get_upstream_sh().git("log", "--oneline", "master") + assert_eq(initial_log, final_log) + + # The PR should still be open + assert_eq(pr.closed, False) +finally: + os.unlink(rules_file) + +ok() diff --git a/test/land/validate_rules_fail.py.test b/test/land/validate_rules_fail.py.test new file mode 100644 index 0000000..affb247 --- /dev/null +++ b/test/land/validate_rules_fail.py.test @@ -0,0 +1,46 @@ +from ghstack.test_prelude import * + +import os +import tempfile +import ghstack.merge_rules +from ghstack.github_fake import GitHubNumber + +init_test() +commit("A") +(diff,) = gh_submit("Initial 1") +pr_url = diff.pr_url + +# Set up PR with files but no approvals +github = get_github() +repo = github.state.repository("pytorch", "pytorch") +pr = github.state.pull_request(repo, GitHubNumber(500)) +pr.files = ["src/core/module.py"] +pr.reviews = [] # No reviews + +# Create a temporary rules file +rules_content = """ +- name: core-changes + patterns: ["src/core/*"] + approved_by: ["maintainer1"] + mandatory_checks_name: [] +""" + +with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + f.write(rules_content) + rules_file = f.name + +try: + # Attempt to land with validation - should raise MergeValidationError + assert_expected_raises_inline( + ghstack.merge_rules.MergeValidationError, + lambda: gh_land(pr_url, validate_rules=True, rules_file=rules_file), + """\ +Merge validation failed for PR #500 +Rule: core-changes +Errors: + - Missing required approval from: maintainer1""", + ) +finally: + os.unlink(rules_file) + +ok() diff --git a/test/land/validate_rules_pass.py.test b/test/land/validate_rules_pass.py.test new file mode 100644 index 0000000..16b301d --- /dev/null +++ b/test/land/validate_rules_pass.py.test @@ -0,0 +1,47 @@ +from ghstack.test_prelude import * + +import os +import tempfile +import ghstack.merge_rules +from ghstack.github_fake import GitHubNumber, PullRequestReview, CheckRun + +init_test() +commit("A") +(diff,) = gh_submit("Initial 1") +pr_url = diff.pr_url + +# Set up PR with files, approvals, and passing checks +github = get_github() +repo = github.state.repository("pytorch", "pytorch") +pr = github.state.pull_request(repo, GitHubNumber(500)) +pr.files = ["src/core/module.py"] +pr.reviews = [PullRequestReview(user="maintainer1", state="APPROVED")] +pr.check_runs = [CheckRun(name="CI", status="completed", conclusion="success")] + +# Create a temporary rules file +rules_content = """ +- name: core-changes + patterns: ["src/core/*"] + approved_by: ["maintainer1"] + mandatory_checks_name: ["CI"] +""" + +with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + f.write(rules_content) + rules_file = f.name + +try: + # Land with validation - should succeed + gh_land(pr_url, validate_rules=True, rules_file=rules_file) + + # Verify the commit was landed + assert_expected_inline( + get_upstream_sh().git("log", "--oneline", "master"), + """\ +d518c9f Commit A (#500) +dc8bfe4 Initial commit""", + ) +finally: + os.unlink(rules_file) + +ok() diff --git a/test/merge_rules/approval_validation.py.test b/test/merge_rules/approval_validation.py.test new file mode 100644 index 0000000..f4f9b0e --- /dev/null +++ b/test/merge_rules/approval_validation.py.test @@ -0,0 +1,93 @@ +from ghstack.test_prelude import * + +import ghstack.merge_rules +from ghstack.github_fake import PullRequestReview, GitHubNumber + +init_test() +commit("A") +(A,) = gh_submit("Initial 1") + +# Set up PR with files and NO approval +github = get_github() +repo = github.state.repository("pytorch", "pytorch") +pr = github.state.pull_request(repo, GitHubNumber(500)) +pr.files = ["src/core/module.py"] +pr.reviews = [] # No reviews yet + +rules = [ + ghstack.merge_rules.MergeRule( + name="core", + patterns=["src/core/*"], + approved_by=["maintainer1"], + mandatory_checks_name=[], + ) +] + +validator = ghstack.merge_rules.MergeValidator(github, "pytorch", "pytorch") +result = validator.validate_pr(500, rules) + +# Should fail - missing approval +assert_eq(result.valid, False) +assert "Missing required approval" in result.errors[0] + +# Add approval from wrong user - should still fail +pr.reviews = [PullRequestReview(user="random_user", state="APPROVED")] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) + +# Add approval from required user - should pass +pr.reviews = [PullRequestReview(user="maintainer1", state="APPROVED")] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +# Test multiple required approvers - any one should work +rules = [ + ghstack.merge_rules.MergeRule( + name="core", + patterns=["src/core/*"], + approved_by=["maintainer1", "maintainer2"], + mandatory_checks_name=[], + ) +] + +pr.reviews = [PullRequestReview(user="maintainer2", state="APPROVED")] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +# Test that CHANGES_REQUESTED doesn't count as approval +pr.reviews = [PullRequestReview(user="maintainer1", state="CHANGES_REQUESTED")] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) + +# Test that latest review state wins +pr.reviews = [ + PullRequestReview(user="maintainer1", state="APPROVED"), + PullRequestReview(user="maintainer1", state="CHANGES_REQUESTED"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) + +# Re-approval after changes_requested should work +pr.reviews = [ + PullRequestReview(user="maintainer1", state="CHANGES_REQUESTED"), + PullRequestReview(user="maintainer1", state="APPROVED"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +# Test "any" special approver - any approval should work +rules = [ + ghstack.merge_rules.MergeRule( + name="any-approval", + patterns=["src/*"], + approved_by=["any"], + mandatory_checks_name=[], + ) +] + +pr.files = ["src/module.py"] +pr.reviews = [PullRequestReview(user="anyone", state="APPROVED")] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +ok() diff --git a/test/merge_rules/check_validation.py.test b/test/merge_rules/check_validation.py.test new file mode 100644 index 0000000..065077c --- /dev/null +++ b/test/merge_rules/check_validation.py.test @@ -0,0 +1,91 @@ +from ghstack.test_prelude import * + +import ghstack.merge_rules +from ghstack.github_fake import CheckRun, GitHubNumber + +init_test() +commit("A") +(A,) = gh_submit("Initial 1") + +github = get_github() +repo = github.state.repository("pytorch", "pytorch") +pr = github.state.pull_request(repo, GitHubNumber(500)) +pr.files = ["src/module.py"] + +rules = [ + ghstack.merge_rules.MergeRule( + name="ci-required", + patterns=["src/*"], + approved_by=["any"], + mandatory_checks_name=["CI", "lint"], + ) +] + +validator = ghstack.merge_rules.MergeValidator(github, "pytorch", "pytorch") + +# Test missing checks - should fail +pr.check_runs = [] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "CI" has not run' in result.errors[0] + +# Test only one check present - should fail +pr.check_runs = [ + CheckRun(name="CI", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "lint" has not run' in result.errors[0] + +# Test failed check - should fail +pr.check_runs = [ + CheckRun(name="CI", status="completed", conclusion="failure"), + CheckRun(name="lint", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "CI" has not passed' in result.errors[0] + +# Test in_progress check - should fail +pr.check_runs = [ + CheckRun(name="CI", status="in_progress", conclusion=None), + CheckRun(name="lint", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "CI" has not completed' in result.errors[0] + +# Test queued check - should fail +pr.check_runs = [ + CheckRun(name="CI", status="queued", conclusion=None), + CheckRun(name="lint", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "CI" has not completed' in result.errors[0] + +# Test passing checks - should pass +pr.check_runs = [ + CheckRun(name="CI", status="completed", conclusion="success"), + CheckRun(name="lint", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +# Test neutral conclusion - should pass (neutral is acceptable) +pr.check_runs = [ + CheckRun(name="CI", status="completed", conclusion="neutral"), + CheckRun(name="lint", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +# Test skipped conclusion - should pass +pr.check_runs = [ + CheckRun(name="CI", status="completed", conclusion="skipped"), + CheckRun(name="lint", status="completed", conclusion="success"), +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +ok() diff --git a/test/merge_rules/ignore_flaky.py.test b/test/merge_rules/ignore_flaky.py.test new file mode 100644 index 0000000..4b63081 --- /dev/null +++ b/test/merge_rules/ignore_flaky.py.test @@ -0,0 +1,61 @@ +from ghstack.test_prelude import * + +import ghstack.merge_rules +from ghstack.github_fake import CheckRun, GitHubNumber + +init_test() +commit("A") +(A,) = gh_submit("Initial 1") + +github = get_github() +repo = github.state.repository("pytorch", "pytorch") +pr = github.state.pull_request(repo, GitHubNumber(500)) +pr.files = ["src/module.py"] +pr.check_runs = [CheckRun(name="flaky-test", status="completed", conclusion="failure")] + +# Without ignore_flaky_failures - should fail +rules = [ + ghstack.merge_rules.MergeRule( + name="test", + patterns=["src/*"], + approved_by=["any"], + mandatory_checks_name=["flaky-test"], + ignore_flaky_failures=False, + ) +] +validator = ghstack.merge_rules.MergeValidator(github, "pytorch", "pytorch") +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "flaky-test" has not passed' in result.errors[0] + +# With ignore_flaky_failures - should pass despite failure +rules = [ + ghstack.merge_rules.MergeRule( + name="test", + patterns=["src/*"], + approved_by=["any"], + mandatory_checks_name=["flaky-test"], + ignore_flaky_failures=True, + ) +] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +# ignore_flaky_failures should NOT ignore missing checks +pr.check_runs = [] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "flaky-test" has not run' in result.errors[0] + +# ignore_flaky_failures should NOT ignore in_progress checks +pr.check_runs = [CheckRun(name="flaky-test", status="in_progress", conclusion=None)] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, False) +assert 'Check "flaky-test" has not completed' in result.errors[0] + +# Success should still pass with ignore_flaky_failures +pr.check_runs = [CheckRun(name="flaky-test", status="completed", conclusion="success")] +result = validator.validate_pr(500, rules) +assert_eq(result.valid, True) + +ok() diff --git a/test/merge_rules/pattern_matching.py.test b/test/merge_rules/pattern_matching.py.test new file mode 100644 index 0000000..2e59cd7 --- /dev/null +++ b/test/merge_rules/pattern_matching.py.test @@ -0,0 +1,82 @@ +from ghstack.test_prelude import * + +import ghstack.merge_rules + +init_test() + +# Create test rules with different patterns +rules = [ + ghstack.merge_rules.MergeRule( + name="core", + patterns=["src/core/*"], + approved_by=[], + mandatory_checks_name=[], + ), + ghstack.merge_rules.MergeRule( + name="tests", + patterns=["test/*.py", "test/**/*.py"], + approved_by=[], + mandatory_checks_name=[], + ), + ghstack.merge_rules.MergeRule( + name="docs", + patterns=["*.md", "docs/*"], + approved_by=[], + mandatory_checks_name=[], + ), +] + +validator = ghstack.merge_rules.MergeValidator(get_github(), "pytorch", "pytorch") + +# Test exact match on first rule +rule = validator.find_matching_rule(["src/core/module.py"], rules) +assert_eq(rule.name, "core") + +# Test match on second rule +rule = validator.find_matching_rule(["test/unit_test.py"], rules) +assert_eq(rule.name, "tests") + +# Test nested test file +rule = validator.find_matching_rule(["test/integration/test_api.py"], rules) +assert_eq(rule.name, "tests") + +# Test docs rule with markdown +rule = validator.find_matching_rule(["README.md"], rules) +assert_eq(rule.name, "docs") + +# Test no match returns None +rule = validator.find_matching_rule(["some_random_file.txt"], rules) +assert_eq(rule, None) + +# Test first matching rule wins when file matches multiple patterns +# The file "docs/guide.md" would match both "*.md" (docs) and "docs/*" (docs) +rule = validator.find_matching_rule(["docs/guide.md"], rules) +assert_eq(rule.name, "docs") + +# Test empty file list returns None +rule = validator.find_matching_rule([], rules) +assert_eq(rule, None) + +# Test multiple files - first matching file determines the rule +rule = validator.find_matching_rule(["README.txt", "src/core/main.py"], rules) +assert_eq(rule.name, "core") + +# Test that patterns are checked in order - first rule to match a file wins +combined_rules = [ + ghstack.merge_rules.MergeRule( + name="specific", + patterns=["src/core/special.py"], + approved_by=[], + mandatory_checks_name=[], + ), + ghstack.merge_rules.MergeRule( + name="general", + patterns=["src/core/*"], + approved_by=[], + mandatory_checks_name=[], + ), +] +rule = validator.find_matching_rule(["src/core/special.py"], combined_rules) +assert_eq(rule.name, "specific") + +ok() diff --git a/test/merge_rules/yaml_parsing.py.test b/test/merge_rules/yaml_parsing.py.test new file mode 100644 index 0000000..73023f2 --- /dev/null +++ b/test/merge_rules/yaml_parsing.py.test @@ -0,0 +1,64 @@ +from ghstack.test_prelude import * + +import ghstack.merge_rules + +init_test() + +# Test basic YAML parsing with all fields +loader = ghstack.merge_rules.MergeRulesLoader(get_github(), "pytorch", "pytorch") +rules = loader._parse_yaml( + """ +- name: core-changes + patterns: ["src/core/*"] + approved_by: ["maintainer1", "pytorch/core-team"] + mandatory_checks_name: ["CI", "lint"] + ignore_flaky_failures: true +""" +) + +assert_eq(len(rules), 1) +assert_eq(rules[0].name, "core-changes") +assert_eq(rules[0].patterns, ["src/core/*"]) +assert_eq(rules[0].approved_by, ["maintainer1", "pytorch/core-team"]) +assert_eq(rules[0].mandatory_checks_name, ["CI", "lint"]) +assert_eq(rules[0].ignore_flaky_failures, True) + +# Test parsing multiple rules +rules = loader._parse_yaml( + """ +- name: rule1 + patterns: ["src/*"] + approved_by: ["user1"] + mandatory_checks_name: [] +- name: rule2 + patterns: ["test/*"] + approved_by: ["user2"] + mandatory_checks_name: ["test-ci"] +""" +) + +assert_eq(len(rules), 2) +assert_eq(rules[0].name, "rule1") +assert_eq(rules[1].name, "rule2") +assert_eq(rules[1].mandatory_checks_name, ["test-ci"]) + +# Test default values +rules = loader._parse_yaml( + """ +- name: minimal + patterns: ["*.py"] +""" +) + +assert_eq(len(rules), 1) +assert_eq(rules[0].name, "minimal") +assert_eq(rules[0].patterns, ["*.py"]) +assert_eq(rules[0].approved_by, []) +assert_eq(rules[0].mandatory_checks_name, []) +assert_eq(rules[0].ignore_flaky_failures, False) + +# Test empty rule list +rules = loader._parse_yaml("[]") +assert_eq(len(rules), 0) + +ok() From c513d29b4b16c6724b04e78d3ab8b93915071d65 Mon Sep 17 00:00:00 2001 From: Eli Uriegas Date: Wed, 4 Feb 2026 13:06:27 -0800 Subject: [PATCH 2/4] Fix linting issues: remove unused imports and trailing whitespace - Remove unused 'import ghstack.merge_rules' from dry_run.py.test and validate_rules_pass.py.test (F401) - Fix trailing whitespace on blank lines (W293) --- test/land/dry_run.py.test | 7 +++---- test/land/validate_rules_pass.py.test | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/test/land/dry_run.py.test b/test/land/dry_run.py.test index 90a4395..28cf92d 100644 --- a/test/land/dry_run.py.test +++ b/test/land/dry_run.py.test @@ -2,7 +2,6 @@ from ghstack.test_prelude import * import os import tempfile -import ghstack.merge_rules from ghstack.github_fake import GitHubNumber, PullRequestReview, CheckRun init_test() @@ -33,14 +32,14 @@ with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: try: # Get the initial state of master initial_log = get_upstream_sh().git("log", "--oneline", "master") - + # Dry run should NOT land the commit gh_land(pr_url, validate_rules=True, dry_run=True, rules_file=rules_file) - + # Verify master is unchanged (no commit was landed) final_log = get_upstream_sh().git("log", "--oneline", "master") assert_eq(initial_log, final_log) - + # The PR should still be open assert_eq(pr.closed, False) finally: diff --git a/test/land/validate_rules_pass.py.test b/test/land/validate_rules_pass.py.test index 16b301d..26ce95b 100644 --- a/test/land/validate_rules_pass.py.test +++ b/test/land/validate_rules_pass.py.test @@ -2,7 +2,6 @@ from ghstack.test_prelude import * import os import tempfile -import ghstack.merge_rules from ghstack.github_fake import GitHubNumber, PullRequestReview, CheckRun init_test() @@ -33,7 +32,7 @@ with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: try: # Land with validation - should succeed gh_land(pr_url, validate_rules=True, rules_file=rules_file) - + # Verify the commit was landed assert_expected_inline( get_upstream_sh().git("log", "--oneline", "master"), From cbe16c102695ce21d092122f4cb43e4ec0f4f16f Mon Sep 17 00:00:00 2001 From: Eli Uriegas Date: Wed, 4 Feb 2026 13:30:17 -0800 Subject: [PATCH 3/4] Fix formatting: list comprehension and import order - Split list comprehension across lines in test_prelude.py - Sort imports alphabetically in approval_validation.py.test --- src/ghstack/test_prelude.py | 3 ++- test/merge_rules/approval_validation.py.test | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ghstack/test_prelude.py b/src/ghstack/test_prelude.py index b3d7218..9fe33ad 100644 --- a/src/ghstack/test_prelude.py +++ b/src/ghstack/test_prelude.py @@ -452,7 +452,8 @@ def set_pr_check_runs( repo = github.state.repository("pytorch", "pytorch") pr = github.state.pull_request(repo, ghstack.github_fake.GitHubNumber(pr_number)) pr.check_runs = [ - ghstack.github_fake.CheckRun(name=n, status=s, conclusion=c) for n, s, c in checks + ghstack.github_fake.CheckRun(name=n, status=s, conclusion=c) + for n, s, c in checks ] diff --git a/test/merge_rules/approval_validation.py.test b/test/merge_rules/approval_validation.py.test index f4f9b0e..d3033c0 100644 --- a/test/merge_rules/approval_validation.py.test +++ b/test/merge_rules/approval_validation.py.test @@ -1,7 +1,7 @@ from ghstack.test_prelude import * import ghstack.merge_rules -from ghstack.github_fake import PullRequestReview, GitHubNumber +from ghstack.github_fake import GitHubNumber, PullRequestReview init_test() commit("A") From 08514d81cf92880d8856528ab5c6741677082369 Mon Sep 17 00:00:00 2001 From: Eli Uriegas Date: Wed, 4 Feb 2026 13:37:19 -0800 Subject: [PATCH 4/4] Fix import formatting per lintrunner - Add blank line between stdlib and third-party imports - Sort imports alphabetically --- test/land/dry_run.py.test | 3 ++- test/land/validate_rules_fail.py.test | 1 + test/land/validate_rules_pass.py.test | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/land/dry_run.py.test b/test/land/dry_run.py.test index 28cf92d..385c8d3 100644 --- a/test/land/dry_run.py.test +++ b/test/land/dry_run.py.test @@ -2,7 +2,8 @@ from ghstack.test_prelude import * import os import tempfile -from ghstack.github_fake import GitHubNumber, PullRequestReview, CheckRun + +from ghstack.github_fake import CheckRun, GitHubNumber, PullRequestReview init_test() commit("A") diff --git a/test/land/validate_rules_fail.py.test b/test/land/validate_rules_fail.py.test index affb247..fdf531b 100644 --- a/test/land/validate_rules_fail.py.test +++ b/test/land/validate_rules_fail.py.test @@ -2,6 +2,7 @@ from ghstack.test_prelude import * import os import tempfile + import ghstack.merge_rules from ghstack.github_fake import GitHubNumber diff --git a/test/land/validate_rules_pass.py.test b/test/land/validate_rules_pass.py.test index 26ce95b..900ad7b 100644 --- a/test/land/validate_rules_pass.py.test +++ b/test/land/validate_rules_pass.py.test @@ -2,7 +2,8 @@ from ghstack.test_prelude import * import os import tempfile -from ghstack.github_fake import GitHubNumber, PullRequestReview, CheckRun + +from ghstack.github_fake import CheckRun, GitHubNumber, PullRequestReview init_test() commit("A")