Repository navigation
fix(gh-wrapper): read GraphQL merge queries from files and --input - #445
Merged
Merged
Conversation
added 2 commits
October 19, 2026 20:50
The GraphQL mergePullRequest guard grepped only the command line. A query passed as -F query=@file or --input file never shows up there, so the mutation reached the real gh and skipped pre-merge-review.sh and the merge-lock. #443 already reads GraphQL queries from inline fields, -F @file values and --input files, and refuses a query it cannot read (stdin, a missing file, \u escapes in an --input file). The merge check now runs through that same reader: _gh_wrapper_block_graphql_pr_publish becomes _gh_wrapper_block_graphql_pr_mutation and its pattern gains mergePullRequest. A merge match keeps the old merge message. The unreadable-query message now names both risks. The check moves from the off-org promotion guard into _gh_wrapper_block_bypass and keys on the subcommand rather than $1, so gh -R o/r api graphql ... is checked too. Matching is no longer line-based, so a multi-line inline query is caught. -f/--raw-field still counts as inline only: gh posts its @file value as a literal string and never reads the file. Tests: section 6 of test-gh-wrapper-off-org-guards.sh adds 15 cases per mode (30 in all); 20 fail on the wrapper before this fix. Closes #444
The REST pulls/N/merge guard tested $1 == api, so a global flag in front of the subcommand (gh -R o/r api repos/o/r/pulls/5/merge -X PUT) skipped it and reached the real gh without pre-merge review or the merge-lock. It now uses the parsed subcommand, as the GraphQL check in the same function does. The commit review flagged this on the previous commit. One case per mode in test-gh-wrapper-off-org-guards.sh; both fail on the wrapper before this fix. Advances #444
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #444.
What changes
mergePullRequestguard used to grep the command line. A query passed as-F query=@fileor--input filenever showed up there, so it reached the realghand skippedpre-merge-review.shand the merge-lock._gh_wrapper_block_graphql_pr_publishis renamed_gh_wrapper_block_graphql_pr_mutation, and its pattern gainsmergePullRequest. A merge match keeps the old merge message. A query the wrapper cannot read (stdin, a missing file,\uescapes in--input) is refused, and the message now names both the merge risk and the off-org risk._gh_wrapper_block_off_org_promotioninto_gh_wrapper_block_bypass, gated on the parsed subcommand. The function makes one pass over the files.gh -R o/r api graphql ...is now checked. The old check required$1 == api.grepwas line-based.graphql mergePullRequest(no longer trips the merge guard. Only a GraphQL endpoint counts.pulls/N/mergeguard had the same$1 == apigap (gh -R o/r api repos/o/r/pulls/5/merge -X PUTpassed). The commit review flagged it, and it now uses the parsed subcommand too.-f query=@fileis not a bypass.gh api --helpsays only-F/--fieldreads@file.-f/--raw-fieldposts the literal string, so the reader keeps treating it as inline, as #443 does. A test pins that.Tests
Section 6 of
bash/tests/test-gh-wrapper-off-org-guards.sh, run in standalone and function mode. It covers inline (plain, spaced, multi-line, after-R, the/graphqlendpoint, the full-URL endpoint),-F @file,--field=@file,-F@file,--input,--input=,\uescape, unreadable stdin,-f @fileas a literal, a REST field naming the mutation, and a REST merge after-R.test-gh-wrapper-*.shfiles pass locally.Separate note: claude-config
hook-block-api-merge.shThe issue guessed that the Bash-tool hook misses
-F query=@file. Tested read-only, with noghrun: the hook got the same{"tool_name":"Bash","tool_input":{"command":...}}JSON its bats tests use, andm.graphqlheld amergePullRequestmutation. It is not a gap. A separate@filerule (claude-config#133) already blocks these:gh api graphql -F query=@m.graphqlgh api graphql -f query=@m.graphqlgh api graphql --field query=@m.graphqlgh api graphql --field=query=@m.graphqlgh api graphql -Fquery=@m.graphqlgh api graphql --input m.graphqlgh api graphql -f query=query{viewer{login}}(control)Not in scope
The commit review asked whether GraphQL
enablePullRequestAutoMergeshould also be refused, since it can merge once checks pass without the merge-lock. This PR does not change it. It is a policy call.