openhound-github 0.6.5__tar.gz → 0.6.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. {openhound_github-0.6.5 → openhound_github-0.6.6}/PKG-INFO +1 -1
  2. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/helpers.py +173 -1
  3. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/resources/organization.py +28 -24
  4. openhound_github-0.6.6/tests/test_repository_rulesets.py +406 -0
  5. openhound_github-0.6.5/tests/test_repository_rulesets.py +0 -241
  6. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/CODE_OF_CONDUCT.md +0 -0
  7. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/CONTRIBUTING.md +0 -0
  8. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GOVERNANCE.md +0 -0
  9. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GitHub-COC-Header.png +0 -0
  10. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GitHub-CONTRIB-Header.png +0 -0
  11. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GitHub-GOV-Header.png +0 -0
  12. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GitHub-Header.png +0 -0
  13. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GitHub-LIC-Header.png +0 -0
  14. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/GitHub-SEC-Header.png +0 -0
  15. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/SECURITY.md +0 -0
  16. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/SO-Dark.svg +0 -0
  17. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/SO-FOSS-Dark.svg +0 -0
  18. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/SO-FOSS-Light.svg +0 -0
  19. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/SO-Light.svg +0 -0
  20. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/workflows/publish.yml +0 -0
  21. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/workflows/release-on-merge.yml +0 -0
  22. {openhound_github-0.6.5 → openhound_github-0.6.6}/.github/workflows/validate-branch.yml +0 -0
  23. {openhound_github-0.6.5 → openhound_github-0.6.6}/.gitignore +0 -0
  24. {openhound_github-0.6.5 → openhound_github-0.6.6}/.gitmodules +0 -0
  25. {openhound_github-0.6.5 → openhound_github-0.6.6}/.pre-commit-config.yaml +0 -0
  26. {openhound_github-0.6.5 → openhound_github-0.6.6}/LICENSE.md +0 -0
  27. {openhound_github-0.6.5 → openhound_github-0.6.6}/README.md +0 -0
  28. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_AddAssignee.md +0 -0
  29. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_AddCollaborator.md +0 -0
  30. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_AddLabel.md +0 -0
  31. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_AddMember.md +0 -0
  32. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_AdminTo.md +0 -0
  33. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ApprovesDeploymentTo.md +0 -0
  34. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_AssignedTo.md +0 -0
  35. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_BypassBranchProtection.md +0 -0
  36. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_BypassPullRequestAllowances.md +0 -0
  37. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CallsWorkflow.md +0 -0
  38. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanAccess.md +0 -0
  39. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanAssumeIdentity.md +0 -0
  40. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanCreateBranch.md +0 -0
  41. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanCreateEnvironment.md +0 -0
  42. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanCreateRepositoryWithRunnerAccess.md +0 -0
  43. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanDeployToEnvironment.md +0 -0
  44. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanEditEnvironment.md +0 -0
  45. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanEditProtection.md +0 -0
  46. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanPwnRequest.md +0 -0
  47. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanReadSecretScanningAlert.md +0 -0
  48. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanUseRunner.md +0 -0
  49. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CanWriteBranch.md +0 -0
  50. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CloseDiscussion.md +0 -0
  51. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CloseIssue.md +0 -0
  52. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ClosePullRequest.md +0 -0
  53. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_Contains.md +0 -0
  54. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ConvertIssuesToDiscussions.md +0 -0
  55. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CreateDiscussionCategory.md +0 -0
  56. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CreateRepository.md +0 -0
  57. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CreateSoloMergeQueueEntry.md +0 -0
  58. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CreateTag.md +0 -0
  59. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_CreateTeam.md +0 -0
  60. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DeleteAlertsCodeScanning.md +0 -0
  61. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DeleteDiscussion.md +0 -0
  62. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DeleteDiscussionComment.md +0 -0
  63. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DeleteIssue.md +0 -0
  64. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DeleteTag.md +0 -0
  65. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DependsOn.md +0 -0
  66. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_DeploysTo.md +0 -0
  67. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditCategoryOnDiscussion.md +0 -0
  68. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditDiscussionCategory.md +0 -0
  69. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditDiscussionComment.md +0 -0
  70. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditRepoAnnouncementBanners.md +0 -0
  71. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditRepoCustomPropertiesValues.md +0 -0
  72. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditRepoMetadata.md +0 -0
  73. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_EditRepoProtections.md +0 -0
  74. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasBaseRole.md +0 -0
  75. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasBranch.md +0 -0
  76. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasEnvironment.md +0 -0
  77. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasExternalIdentity.md +0 -0
  78. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasJob.md +0 -0
  79. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasMember.md +0 -0
  80. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasPersonalAccessToken.md +0 -0
  81. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasPersonalAccessTokenRequest.md +0 -0
  82. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasRole.md +0 -0
  83. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasRunner.md +0 -0
  84. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasSamlIdentityProvider.md +0 -0
  85. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasSecret.md +0 -0
  86. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasStep.md +0 -0
  87. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasVariable.md +0 -0
  88. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_HasWorkflow.md +0 -0
  89. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_InheritedFrom.md +0 -0
  90. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_InstalledAs.md +0 -0
  91. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_InviteMember.md +0 -0
  92. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_IsEligibleFor.md +0 -0
  93. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_JumpMergeQueue.md +0 -0
  94. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageDeployKeys.md +0 -0
  95. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageDiscussionBadges.md +0 -0
  96. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageOrganizationWebhooks.md +0 -0
  97. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageRepoSecurityProducts.md +0 -0
  98. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageSecurityProducts.md +0 -0
  99. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageSettingsMergeTypes.md +0 -0
  100. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageSettingsPages.md +0 -0
  101. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageSettingsProjects.md +0 -0
  102. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageSettingsWiki.md +0 -0
  103. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageTopics.md +0 -0
  104. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ManageWebhooks.md +0 -0
  105. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_MapsToUser.md +0 -0
  106. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_MarkAsDuplicate.md +0 -0
  107. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_MatchesEnvironmentPolicy.md +0 -0
  108. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_MemberOf.md +0 -0
  109. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_OrgBypassCodeScanningDismissalRequests.md +0 -0
  110. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_OrgBypassSecretScanningClosureRequests.md +0 -0
  111. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_OrgReviewAndManageSecretScanningBypassRequests.md +0 -0
  112. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_OrgReviewAndManageSecretScanningClosureRequests.md +0 -0
  113. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_Owns.md +0 -0
  114. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ProtectedBy.md +0 -0
  115. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_PushProtectedBranch.md +0 -0
  116. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReadCodeScanning.md +0 -0
  117. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReadOrganizationActionsUsageMetrics.md +0 -0
  118. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReadOrganizationCustomOrgRole.md +0 -0
  119. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReadOrganizationCustomRepoRole.md +0 -0
  120. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReadRepoContents.md +0 -0
  121. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_RemoveAssignee.md +0 -0
  122. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_RemoveLabel.md +0 -0
  123. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReopenDiscussion.md +0 -0
  124. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReopenIssue.md +0 -0
  125. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ReopenPullRequest.md +0 -0
  126. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_RequestPrReview.md +0 -0
  127. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ResolveDependabotAlerts.md +0 -0
  128. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ResolveSecretScanningAlerts.md +0 -0
  129. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_RestrictionsCanPush.md +0 -0
  130. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_RunOrgMigration.md +0 -0
  131. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_SetInteractionLimits.md +0 -0
  132. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_SetIssueType.md +0 -0
  133. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_SetMilestone.md +0 -0
  134. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_SetSocialPreview.md +0 -0
  135. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_SyncedTo.md +0 -0
  136. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ToggleDiscussionAnswer.md +0 -0
  137. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ToggleDiscussionCommentMinimize.md +0 -0
  138. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_TransferRepository.md +0 -0
  139. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_UsesSecret.md +0 -0
  140. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_UsesVariable.md +0 -0
  141. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ValidToken.md +0 -0
  142. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ViewDependabotAlerts.md +0 -0
  143. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_ViewSecretScanningAlerts.md +0 -0
  144. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteCodeScanning.md +0 -0
  145. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteOrganizationActionsSecrets.md +0 -0
  146. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteOrganizationActionsSettings.md +0 -0
  147. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteOrganizationActionsVariables.md +0 -0
  148. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteOrganizationCustomOrgRole.md +0 -0
  149. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteOrganizationCustomRepoRole.md +0 -0
  150. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteOrganizationNetworkConfigurations.md +0 -0
  151. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteRepoContents.md +0 -0
  152. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/edges/GH_WriteRepoPullRequests.md +0 -0
  153. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_App.md +0 -0
  154. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_AppInstallation.md +0 -0
  155. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_Branch.md +0 -0
  156. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_BranchProtectionRule.md +0 -0
  157. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_EnterpriseRunner.md +0 -0
  158. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_EnterpriseRunnerGroup.md +0 -0
  159. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_Environment.md +0 -0
  160. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_EnvironmentBranchPolicy.md +0 -0
  161. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_EnvironmentSecret.md +0 -0
  162. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_EnvironmentVariable.md +0 -0
  163. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_ExternalIdentity.md +0 -0
  164. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_OrgRole.md +0 -0
  165. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_OrgRunner.md +0 -0
  166. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_OrgRunnerGroup.md +0 -0
  167. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_OrgSecret.md +0 -0
  168. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_OrgVariable.md +0 -0
  169. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_Organization.md +0 -0
  170. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_PersonalAccessToken.md +0 -0
  171. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_PersonalAccessTokenRequest.md +0 -0
  172. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_RepoRole.md +0 -0
  173. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_RepoRunner.md +0 -0
  174. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_RepoSecret.md +0 -0
  175. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_RepoVariable.md +0 -0
  176. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_Repository.md +0 -0
  177. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_SamlIdentityProvider.md +0 -0
  178. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_SecretScanningAlert.md +0 -0
  179. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_Team.md +0 -0
  180. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_TeamRole.md +0 -0
  181. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_User.md +0 -0
  182. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_Workflow.md +0 -0
  183. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_WorkflowJob.md +0 -0
  184. {openhound_github-0.6.5 → openhound_github-0.6.6}/descriptions/nodes/GH_WorkflowStep.md +0 -0
  185. {openhound_github-0.6.5 → openhound_github-0.6.6}/docs/og-docs.json +0 -0
  186. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-all-repo-admin-role.json +0 -0
  187. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-app-installations-all-repos.json +0 -0
  188. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-apps-all-repos.json +0 -0
  189. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-external-identities-owners.json +0 -0
  190. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-organizations.json +0 -0
  191. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-owner-users.json +0 -0
  192. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-owners-role.json +0 -0
  193. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-pats-all-repos.json +0 -0
  194. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-privilege-escalation-roles.json +0 -0
  195. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-privilege-escalation-users.json +0 -0
  196. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/privilege_zone_rules/t0-saml-identity-providers.json +0 -0
  197. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/README.md +0 -0
  198. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/actions-sha-pinning-not-required.json +0 -0
  199. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/active-leaked-secrets.json +0 -0
  200. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/advanced-security-disabled-new-repos.json +0 -0
  201. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/all-actions-allowed.json +0 -0
  202. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/app-installations-all-repos.json +0 -0
  203. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-admins-not-enforced.json +0 -0
  204. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-deletions-allowed.json +0 -0
  205. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-force-pushes.json +0 -0
  206. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-no-code-owner-reviews.json +0 -0
  207. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-no-pr-reviews.json +0 -0
  208. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-no-status-checks.json +0 -0
  209. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-self-approval.json +0 -0
  210. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/branch-protection-stale-reviews.json +0 -0
  211. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/bypass-pr-requirements.json +0 -0
  212. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/dangerous-branch-perms.json +0 -0
  213. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/default-repository-permissions.json +0 -0
  214. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/demo-sso-to-cloud-round-trip.json +0 -0
  215. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/dependabot-alerts-disabled-new-repos.json +0 -0
  216. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/dependabot-updates-disabled-new-repos.json +0 -0
  217. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/dependency-graph-disabled-new-repos.json +0 -0
  218. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/environments-admin-bypass.json +0 -0
  219. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/expired-pats.json +0 -0
  220. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/external-identities-without-scim.json +0 -0
  221. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/github-to-azure-identity.json +0 -0
  222. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/global-repo-perms.json +0 -0
  223. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/hybrid-identities.json +0 -0
  224. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/members-can-change-repo-visibility.json +0 -0
  225. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/members-can-create-pages.json +0 -0
  226. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/members-can-create-public-repos.json +0 -0
  227. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/members-can-delete-repos.json +0 -0
  228. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/members-can-fork-private-repos.json +0 -0
  229. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/members-can-invite-outside-collaborators.json +0 -0
  230. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/org-owners.json +0 -0
  231. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/orgs-without-2fa.json +0 -0
  232. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/pats-all-repo-access.json +0 -0
  233. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/pending-pat-requests.json +0 -0
  234. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/private-repos-forking-allowed.json +0 -0
  235. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/privileged-custom-org-roles.json +0 -0
  236. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/privileged-hybrid-identities.json +0 -0
  237. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/public-repos.json +0 -0
  238. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/push-protection-disabled-new-repos.json +0 -0
  239. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/push-to-protected-branches.json +0 -0
  240. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/repos-secret-scanning-disabled.json +0 -0
  241. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/repos-vulnerable-to-workflow-secret-exfil.json +0 -0
  242. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/repository-workflows.json +0 -0
  243. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/saml-configuration.json +0 -0
  244. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/secret-scanning-alerts.json +0 -0
  245. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/secret-scanning-disabled-new-repos.json +0 -0
  246. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/secrets-reachable-by-user.json +0 -0
  247. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/team-membership-admin.json +0 -0
  248. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/team-structure.json +0 -0
  249. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/unprotected-branches.json +0 -0
  250. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/unprotected-default-branch-with-workflow.json +0 -0
  251. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/unprotected-default-branches.json +0 -0
  252. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/saved_searches/web-commit-signoff-not-required.json +0 -0
  253. {openhound_github-0.6.5 → openhound_github-0.6.6}/extension/schema.json +0 -0
  254. {openhound_github-0.6.5 → openhound_github-0.6.6}/justfile +0 -0
  255. {openhound_github-0.6.5 → openhound_github-0.6.6}/pyproject.toml +0 -0
  256. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/main.py +0 -0
  257. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/__init__.py +0 -0
  258. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/auth.py +0 -0
  259. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/extension.yaml +0 -0
  260. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/graph.py +0 -0
  261. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/graphql.py +0 -0
  262. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/kinds/edges.py +0 -0
  263. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/kinds/nodes.py +0 -0
  264. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/lookup.py +0 -0
  265. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/main.py +0 -0
  266. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/__init__.py +0 -0
  267. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/actions_permission.py +0 -0
  268. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/app_installation.py +0 -0
  269. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/branch.py +0 -0
  270. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/branch_pr_bypass_allowance.py +0 -0
  271. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/branch_protection_rule.py +0 -0
  272. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/branch_push_allowance.py +0 -0
  273. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise.py +0 -0
  274. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_admin.py +0 -0
  275. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_helpers.py +0 -0
  276. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_managed_user.py +0 -0
  277. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_member.py +0 -0
  278. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_organization.py +0 -0
  279. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_role.py +0 -0
  280. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_role_team.py +0 -0
  281. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_role_user.py +0 -0
  282. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_team.py +0 -0
  283. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_team_member.py +0 -0
  284. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_team_organization.py +0 -0
  285. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_team_role.py +0 -0
  286. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/enterprise_user.py +0 -0
  287. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/env_secret.py +0 -0
  288. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/env_variable.py +0 -0
  289. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/environment.py +0 -0
  290. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/environment_branch_policy.py +0 -0
  291. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/external_identity.py +0 -0
  292. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/org.py +0 -0
  293. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/org_role.py +0 -0
  294. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/org_role_member.py +0 -0
  295. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/org_role_team.py +0 -0
  296. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/org_secret.py +0 -0
  297. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/org_variable.py +0 -0
  298. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/personal_access_token.py +0 -0
  299. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/personal_access_token_access.py +0 -0
  300. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/personal_access_token_request.py +0 -0
  301. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/projected_enterprise_team.py +0 -0
  302. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/repo_role_assignment.py +0 -0
  303. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/repository.py +0 -0
  304. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/repository_role.py +0 -0
  305. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/repository_secret.py +0 -0
  306. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/repository_variable.py +0 -0
  307. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/runner.py +0 -0
  308. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/saml_assertion_consumer_service.py +0 -0
  309. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/saml_helpers.py +0 -0
  310. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/saml_issuer.py +0 -0
  311. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/saml_provider.py +0 -0
  312. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/saml_service_provider.py +0 -0
  313. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/scim_user.py +0 -0
  314. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/secret_scanning_alert.py +0 -0
  315. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/team.py +0 -0
  316. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/team_member.py +0 -0
  317. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/team_role.py +0 -0
  318. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/user.py +0 -0
  319. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/workflow.py +0 -0
  320. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/workflow_job.py +0 -0
  321. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/models/workflow_step.py +0 -0
  322. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/resources/__init__.py +0 -0
  323. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/resources/enterprise.py +0 -0
  324. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/runner_ids.py +0 -0
  325. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/source.py +0 -0
  326. {openhound_github-0.6.5 → openhound_github-0.6.6}/src/openhound_github/transforms.py +0 -0
  327. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_app_auth.py +0 -0
  328. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_branch_collection_gate.py +0 -0
  329. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_branch_protection_traversability.py +0 -0
  330. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/actions-permissions.json +0 -0
  331. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/actionsecrets.json +0 -0
  332. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/app.json +0 -0
  333. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/appinstalls.json +0 -0
  334. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/branch-policies.json +0 -0
  335. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/branch-prot-rules.json +0 -0
  336. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/branches.json +0 -0
  337. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/custom-roles.json +0 -0
  338. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/environments.json +0 -0
  339. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/org-pat-request.json +0 -0
  340. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/org-pat.json +0 -0
  341. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/org-role-teams.json +0 -0
  342. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/org-role-users.json +0 -0
  343. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/org-roles.json +0 -0
  344. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/org.json +0 -0
  345. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/pat-repositories.json +0 -0
  346. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/repo-collaborators.json +0 -0
  347. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/repo-teams.json +0 -0
  348. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/repositories.json +0 -0
  349. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/scimusers.json +0 -0
  350. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/secrets.json +0 -0
  351. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/secretscanningalert.json +0 -0
  352. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/selected-org-secrets.json +0 -0
  353. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/variables.json +0 -0
  354. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_data/api/workflows.json +0 -0
  355. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_enterprise_capabilities.py +0 -0
  356. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_enterprise_organization.py +0 -0
  357. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_enterprise_resources.py +0 -0
  358. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_environment_model.py +0 -0
  359. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_environment_resources.py +0 -0
  360. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_environment_reviewer_lookup.py +0 -0
  361. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_error_resilience.py +0 -0
  362. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_github_app_retry.py +0 -0
  363. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_github_rest_client.py +0 -0
  364. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_github_retry.py +0 -0
  365. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_helpers.py +0 -0
  366. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_lookup.py +0 -0
  367. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_org_scim_resources.py +0 -0
  368. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_org_secret_models.py +0 -0
  369. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_runner_ids.py +0 -0
  370. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_runner_models.py +0 -0
  371. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_runner_resources.py +0 -0
  372. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_saml_helpers.py +0 -0
  373. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_saml_models.py +0 -0
  374. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_scim_models.py +0 -0
  375. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_source_org_canonicalization.py +0 -0
  376. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_variable_models.py +0 -0
  377. {openhound_github-0.6.5 → openhound_github-0.6.6}/tests/test_workflow_model.py +0 -0
  378. {openhound_github-0.6.5 → openhound_github-0.6.6}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openhound-github
3
- Version: 0.6.5
3
+ Version: 0.6.6
4
4
  Summary: Github collector for OpenHound
5
5
  Author-email: "jdreijer@specterops.io" <jdreijer@specterops.io>, Jared Atkinson <jatkinson@specterops.io>
6
6
  License-File: LICENSE.md
@@ -1,11 +1,13 @@
1
1
  import logging
2
2
  import time
3
- from typing import Optional
3
+ from collections.abc import Iterator
4
+ from typing import Any, Optional
4
5
  from urllib.parse import urlparse
5
6
 
6
7
  from dlt.common import jsonpath
7
8
  from dlt.sources.helpers import requests
8
9
  from dlt.sources.helpers.rest_client.auth import AuthConfigBase
10
+ from dlt.sources.helpers.rest_client.client import RESTClient
9
11
  from dlt.sources.helpers.rest_client.paginators import (
10
12
  JSONResponseCursorPaginator,
11
13
  )
@@ -20,6 +22,22 @@ class GraphQLPaginationError(RuntimeError):
20
22
  pass
21
23
 
22
24
 
25
+ class AdaptiveGraphQLPageError(RuntimeError):
26
+ """A terminal GraphQL page failure with adaptive pagination context."""
27
+
28
+ def __init__(
29
+ self,
30
+ *,
31
+ cursor: str | None,
32
+ page_size: int,
33
+ error: BaseException,
34
+ ) -> None:
35
+ super().__init__(str(error))
36
+ self.cursor = cursor
37
+ self.page_size = page_size
38
+ self.error = error
39
+
40
+
23
41
  def scim_skip_reason(exception: BaseException) -> str | None:
24
42
  """Return a user-facing reason for expected SCIM API unavailability."""
25
43
  if not isinstance(exception, requests.HTTPError) or exception.response is None:
@@ -56,6 +74,10 @@ class GraphQLCursorPaginator(JSONResponseCursorPaginator):
56
74
  self.has_next_field = has_next_field
57
75
  self.allow_missing_page_info = allow_missing_page_info
58
76
 
77
+ @property
78
+ def next_cursor(self) -> str | None:
79
+ return self._next_reference
80
+
59
81
  def init_request(self, request: "Request") -> None:
60
82
  self._next_reference = None
61
83
  self._has_next_page = True
@@ -149,6 +171,156 @@ class GraphQLCursorPaginator(JSONResponseCursorPaginator):
149
171
  variables[self.cursor_variable] = self._next_reference
150
172
 
151
173
 
174
+ def _graphql_gateway_status(exception: BaseException) -> int | None:
175
+ if not isinstance(exception, requests.HTTPError) or exception.response is None:
176
+ return None
177
+
178
+ status_code = exception.response.status_code
179
+ if status_code in (502, 504):
180
+ return status_code
181
+ return None
182
+
183
+
184
+ def adaptive_graphql_paginate(
185
+ client: RESTClient,
186
+ *,
187
+ query: str,
188
+ variables: dict[str, Any],
189
+ page_info_path: str,
190
+ cursor_variable: str = "after",
191
+ page_size_variable: str = "count",
192
+ page_sizes: tuple[int, ...] = (100, 50, 25),
193
+ allow_missing_page_info: bool = False,
194
+ resource_name: str = "graphql",
195
+ scope_name: str | None = None,
196
+ scope_value: str | None = None,
197
+ log: logging.Logger | None = None,
198
+ ) -> Iterator[dict[str, Any]]:
199
+ """Yield GraphQL pages while adapting connection size after gateway failures.
200
+
201
+ The underlying HTTP session still owns ordinary request retries. This helper
202
+ handles the remaining terminal 502/504 case by retrying the same cursor with
203
+ progressively smaller connection sizes.
204
+ """
205
+ if not page_sizes or any(page_size <= 0 for page_size in page_sizes):
206
+ raise ValueError("page_sizes must contain positive integers")
207
+ if tuple(sorted(set(page_sizes), reverse=True)) != page_sizes:
208
+ raise ValueError("page_sizes must be unique and in descending order")
209
+
210
+ logger_instance = log or logger
211
+ base_page_size = page_sizes[0]
212
+ cursor = variables.get(cursor_variable)
213
+ if cursor is not None and not isinstance(cursor, str):
214
+ raise ValueError(f"{cursor_variable} must be a string or None")
215
+
216
+ stable_page_size = base_page_size
217
+ probe_base_page_size = False
218
+
219
+ while True:
220
+ if stable_page_size == base_page_size:
221
+ candidate_sizes = page_sizes
222
+ probing_base_page_size = False
223
+ elif probe_base_page_size:
224
+ candidate_sizes = (base_page_size,) + tuple(
225
+ page_size
226
+ for page_size in page_sizes
227
+ if page_size <= stable_page_size
228
+ )
229
+ probing_base_page_size = True
230
+ else:
231
+ candidate_sizes = tuple(
232
+ page_size
233
+ for page_size in page_sizes
234
+ if page_size <= stable_page_size
235
+ )
236
+ probing_base_page_size = False
237
+
238
+ page_data: dict[str, Any] | None = None
239
+ paginator: GraphQLCursorPaginator | None = None
240
+ successful_page_size: int | None = None
241
+
242
+ for index, page_size in enumerate(candidate_sizes):
243
+ request_variables = {
244
+ **variables,
245
+ cursor_variable: cursor,
246
+ page_size_variable: page_size,
247
+ }
248
+ try:
249
+ response = client.post(
250
+ "/graphql",
251
+ json={"query": query, "variables": request_variables},
252
+ )
253
+ response.raise_for_status()
254
+ response_json = response.json()
255
+ paginator = GraphQLCursorPaginator(
256
+ page_info_path=page_info_path,
257
+ cursor_variable=cursor_variable,
258
+ cursor_field="endCursor",
259
+ has_next_field="hasNextPage",
260
+ allow_missing_page_info=allow_missing_page_info,
261
+ )
262
+ paginator.update_state(response)
263
+ page_data = response_json.get("data")
264
+ if not isinstance(page_data, dict):
265
+ raise GraphQLPaginationError(
266
+ "GraphQL response data must be an object"
267
+ )
268
+ successful_page_size = page_size
269
+ break
270
+ except Exception as error:
271
+ status_code = _graphql_gateway_status(error)
272
+ next_page_size = (
273
+ candidate_sizes[index + 1]
274
+ if status_code is not None and index + 1 < len(candidate_sizes)
275
+ else None
276
+ )
277
+ if next_page_size is not None:
278
+ scope = (
279
+ f" {scope_name} '{scope_value}'"
280
+ if scope_name and scope_value
281
+ else ""
282
+ )
283
+ logger_instance.warning(
284
+ "Adaptive GraphQL pagination for resource '%s'%s retrying "
285
+ "cursor %r with page size %d after HTTP %d at page size %d",
286
+ resource_name,
287
+ scope,
288
+ cursor,
289
+ next_page_size,
290
+ status_code,
291
+ page_size,
292
+ )
293
+ continue
294
+ raise AdaptiveGraphQLPageError(
295
+ cursor=cursor,
296
+ page_size=page_size,
297
+ error=error,
298
+ ) from error
299
+
300
+ if page_data is None or paginator is None or successful_page_size is None:
301
+ raise GraphQLPaginationError("GraphQL page did not produce data")
302
+
303
+ yield page_data
304
+
305
+ if successful_page_size == base_page_size:
306
+ stable_page_size = base_page_size
307
+ probe_base_page_size = False
308
+ elif stable_page_size == base_page_size:
309
+ stable_page_size = successful_page_size
310
+ probe_base_page_size = True
311
+ elif probing_base_page_size:
312
+ stable_page_size = successful_page_size
313
+ probe_base_page_size = False
314
+ elif successful_page_size < stable_page_size:
315
+ stable_page_size = successful_page_size
316
+ probe_base_page_size = False
317
+
318
+ if not paginator.has_next_page:
319
+ break
320
+
321
+ cursor = paginator.next_cursor
322
+
323
+
152
324
  def _response_message(response: requests.Response) -> str:
153
325
  try:
154
326
  response_data = response.json()
@@ -22,7 +22,12 @@ from openhound_github.graphql import (
22
22
  TEAM_MEMBERS_OVERFLOW_QUERY,
23
23
  TEAMS_QUERY,
24
24
  )
25
- from openhound_github.helpers import GraphQLCursorPaginator, scim_skip_reason
25
+ from openhound_github.helpers import (
26
+ AdaptiveGraphQLPageError,
27
+ GraphQLCursorPaginator,
28
+ adaptive_graphql_paginate,
29
+ scim_skip_reason,
30
+ )
26
31
  from openhound_github.main import app
27
32
  from openhound_github.models import (
28
33
  ActionPermission,
@@ -926,25 +931,17 @@ def repositories_graphql(ctx: SourceContext):
926
931
  repository_cursor: str | None = None
927
932
  emitted_repositories = 0
928
933
  try:
929
- paginator = GraphQLCursorPaginator(
934
+ for page_data in adaptive_graphql_paginate(
935
+ client,
936
+ query=REPO_REFS_QUERY,
937
+ variables={"login": org_name, "after": None},
930
938
  page_info_path="data.organization.repositories.pageInfo",
931
- cursor_variable="after",
932
- cursor_field="endCursor",
933
- has_next_field="hasNextPage",
934
- )
935
- data = {
936
- "query": REPO_REFS_QUERY,
937
- "variables": {"login": org_name, "count": 100, "after": None},
938
- }
939
-
940
- for page_data in client.paginate(
941
- "/graphql",
942
- method="POST",
943
- json=data,
944
- paginator=paginator,
945
- data_selector="data",
939
+ resource_name="repositories_graphql",
940
+ scope_name="organization",
941
+ scope_value=org_name,
942
+ log=logger,
946
943
  ):
947
- repos_page = page_data[0]["organization"]["repositories"]
944
+ repos_page = page_data["organization"]["repositories"]
948
945
  for repo in repos_page["nodes"]:
949
946
  repo_record = {**repo}
950
947
  branch_rulesets = repo_record.pop("branchRulesets", None) or {}
@@ -959,22 +956,29 @@ def repositories_graphql(ctx: SourceContext):
959
956
  if isinstance(page_info, dict):
960
957
  repository_cursor = page_info.get("endCursor")
961
958
  except Exception as e:
959
+ failure = e.error if isinstance(e, AdaptiveGraphQLPageError) else e
960
+ failure_cursor = (
961
+ e.cursor if isinstance(e, AdaptiveGraphQLPageError) else repository_cursor
962
+ )
963
+ page_size = e.page_size if isinstance(e, AdaptiveGraphQLPageError) else None
962
964
  logger.error(
963
965
  "Error in resource 'repositories_graphql' processing organization '%s' "
964
- "at repository cursor %r after emitting %d repositories "
966
+ "at repository cursor %r with page size %r after emitting %d repositories "
965
967
  "(%s): %s",
966
968
  org_name,
967
- repository_cursor,
969
+ failure_cursor,
970
+ page_size,
968
971
  emitted_repositories,
969
- type(e).__name__,
970
- e,
972
+ type(failure).__name__,
973
+ failure,
971
974
  extra={
972
975
  "resource": "repositories_graphql",
973
976
  "phase": "resource_iteration",
974
977
  "org_name": org_name,
975
- "repository_cursor": repository_cursor,
978
+ "repository_cursor": failure_cursor,
979
+ "page_size": page_size,
976
980
  "emitted_repositories": emitted_repositories,
977
- "error_type": type(e).__name__,
981
+ "error_type": type(failure).__name__,
978
982
  },
979
983
  )
980
984
  continue
@@ -0,0 +1,406 @@
1
+ import duckdb
2
+ import json
3
+ import logging
4
+ from unittest.mock import MagicMock
5
+
6
+ import requests
7
+
8
+ from openhound_github.lookup import GithubLookup
9
+ from openhound_github.models.repository import Repository
10
+ from openhound_github.resources.organization import (
11
+ OrgContext,
12
+ SourceContext,
13
+ repositories_graphql,
14
+ )
15
+
16
+
17
+ class _FakeClient:
18
+ def __init__(self, *responses: requests.Response | BaseException) -> None:
19
+ self.responses = list(responses) or [
20
+ _graphql_response(_repository_page_data("R_1", "repo", branch_ruleset_count=2))
21
+ ]
22
+ self.request_variables: list[dict[str, object]] = []
23
+
24
+ def post(self, _path: str, *, json: dict[str, object]):
25
+ variables = json["variables"]
26
+ assert isinstance(variables, dict)
27
+ self.request_variables.append({**variables})
28
+ response = self.responses.pop(0)
29
+ if isinstance(response, BaseException):
30
+ raise response
31
+ return response
32
+
33
+
34
+ def _repository_page_data(
35
+ repository_id: str,
36
+ repository_name: str,
37
+ *,
38
+ branch_ruleset_count: int | None = None,
39
+ repository_end_cursor: str | None = None,
40
+ repositories_has_next_page: bool = False,
41
+ ) -> dict:
42
+ return {
43
+ "organization": {
44
+ "repositories": {
45
+ "pageInfo": {
46
+ "endCursor": repository_end_cursor,
47
+ "hasNextPage": repositories_has_next_page,
48
+ },
49
+ "nodes": [
50
+ {
51
+ "id": repository_id,
52
+ "name": repository_name,
53
+ "branchRulesets": {"totalCount": branch_ruleset_count},
54
+ "refs": {
55
+ "nodes": [],
56
+ "pageInfo": {
57
+ "endCursor": None,
58
+ "hasNextPage": False,
59
+ },
60
+ },
61
+ }
62
+ ]
63
+ }
64
+ }
65
+ }
66
+
67
+
68
+ def _graphql_response(
69
+ data: dict[str, object] | None = None,
70
+ *,
71
+ status_code: int = 200,
72
+ text: str | None = None,
73
+ ) -> requests.Response:
74
+ response = requests.Response()
75
+ response.status_code = status_code
76
+ if text is not None:
77
+ response._content = text.encode("utf-8")
78
+ else:
79
+ response._content = json.dumps({"data": data or {}}).encode("utf-8")
80
+ response.request = requests.Request("POST", "https://api.github.com/graphql").prepare()
81
+ return response
82
+
83
+
84
+ def _request_pages(client: _FakeClient) -> list[tuple[object, object]]:
85
+ return [
86
+ (variables["after"], variables["count"])
87
+ for variables in client.request_variables
88
+ ]
89
+
90
+
91
+ def _make_repository() -> Repository:
92
+ return Repository(
93
+ id=1,
94
+ node_id="R_1",
95
+ name="repo",
96
+ full_name="org/repo",
97
+ private=False,
98
+ size=0,
99
+ owner={
100
+ "login": "octocat",
101
+ "id": 1,
102
+ "node_id": "U_1",
103
+ "avatar_url": "",
104
+ "gravatar_id": "",
105
+ "url": "",
106
+ "html_url": "",
107
+ "followers_url": "",
108
+ "following_url": "",
109
+ "gists_url": "",
110
+ "starred_url": "",
111
+ "subscriptions_url": "",
112
+ "organizations_url": "",
113
+ "repos_url": "",
114
+ "events_url": "",
115
+ "received_events_url": "",
116
+ "type": "User",
117
+ "site_admin": False,
118
+ },
119
+ org_login="org",
120
+ )
121
+
122
+
123
+ def test_repositories_graphql_flattens_branch_ruleset_count() -> None:
124
+ client = _FakeClient()
125
+ ctx = SourceContext(
126
+ client=client,
127
+ organizations=[OrgContext(client=client, org_name="org")],
128
+ )
129
+
130
+ rows = list(repositories_graphql.__wrapped__(ctx))
131
+
132
+ assert rows == [
133
+ {
134
+ "id": "R_1",
135
+ "name": "repo",
136
+ "refs": {
137
+ "nodes": [],
138
+ "pageInfo": {"endCursor": None, "hasNextPage": False},
139
+ },
140
+ "branch_ruleset_count": 2,
141
+ "org_login": "org",
142
+ }
143
+ ]
144
+
145
+
146
+ def test_repositories_graphql_logs_cursor_and_emitted_count_on_page_failure(
147
+ caplog,
148
+ ) -> None:
149
+ client = _FakeClient(
150
+ _graphql_response(
151
+ _repository_page_data(
152
+ "R_1",
153
+ "repo",
154
+ branch_ruleset_count=2,
155
+ repository_end_cursor="cursor-page-2",
156
+ repositories_has_next_page=True,
157
+ )
158
+ ),
159
+ ConnectionError("GraphQL page failed after retries"),
160
+ )
161
+ ctx = SourceContext(
162
+ client=client,
163
+ organizations=[OrgContext(client=client, org_name="org")],
164
+ )
165
+
166
+ with caplog.at_level(logging.ERROR, logger="openhound_github.resources.organization"):
167
+ rows = list(repositories_graphql.__wrapped__(ctx))
168
+
169
+ assert len(rows) == 1
170
+ assert (
171
+ "Error in resource 'repositories_graphql' processing organization 'org' "
172
+ "at repository cursor 'cursor-page-2' with page size 100 "
173
+ "after emitting 1 repositories "
174
+ "(ConnectionError): GraphQL page failed after retries"
175
+ ) in caplog.text
176
+ assert _request_pages(client) == [(None, 100), ("cursor-page-2", 100)]
177
+
178
+
179
+ def test_repositories_graphql_emits_all_repository_pages() -> None:
180
+ client = _FakeClient(
181
+ _graphql_response(
182
+ _repository_page_data(
183
+ "R_1",
184
+ "repo-1",
185
+ branch_ruleset_count=2,
186
+ repository_end_cursor="cursor-page-2",
187
+ repositories_has_next_page=True,
188
+ )
189
+ ),
190
+ _graphql_response(_repository_page_data("R_2", "repo-2", branch_ruleset_count=0)),
191
+ )
192
+ ctx = SourceContext(
193
+ client=client,
194
+ organizations=[OrgContext(client=client, org_name="org")],
195
+ )
196
+
197
+ rows = list(repositories_graphql.__wrapped__(ctx))
198
+
199
+ assert [(row["id"], row["branch_ruleset_count"]) for row in rows] == [
200
+ ("R_1", 2),
201
+ ("R_2", 0),
202
+ ]
203
+ assert _request_pages(client) == [(None, 100), ("cursor-page-2", 100)]
204
+
205
+
206
+ def test_repositories_graphql_retries_gateway_failure_with_smaller_page_size(
207
+ caplog,
208
+ ) -> None:
209
+ client = _FakeClient(
210
+ _graphql_response(
211
+ _repository_page_data(
212
+ "R_1",
213
+ "repo-1",
214
+ repository_end_cursor="cursor-page-2",
215
+ repositories_has_next_page=True,
216
+ )
217
+ ),
218
+ _graphql_response(status_code=502, text="bad gateway"),
219
+ _graphql_response(
220
+ _repository_page_data(
221
+ "R_2",
222
+ "repo-2",
223
+ repository_end_cursor="cursor-page-3",
224
+ repositories_has_next_page=True,
225
+ )
226
+ ),
227
+ _graphql_response(_repository_page_data("R_3", "repo-3")),
228
+ )
229
+ ctx = SourceContext(
230
+ client=client,
231
+ organizations=[OrgContext(client=client, org_name="org")],
232
+ )
233
+
234
+ with caplog.at_level(logging.WARNING, logger="openhound_github.resources.organization"):
235
+ rows = list(repositories_graphql.__wrapped__(ctx))
236
+
237
+ assert [row["id"] for row in rows] == ["R_1", "R_2", "R_3"]
238
+ assert _request_pages(client) == [
239
+ (None, 100),
240
+ ("cursor-page-2", 100),
241
+ ("cursor-page-2", 50),
242
+ ("cursor-page-3", 100),
243
+ ]
244
+ assert (
245
+ "retrying cursor 'cursor-page-2' with page size 50 after HTTP 502 "
246
+ "at page size 100"
247
+ ) in caplog.text
248
+
249
+
250
+ def test_repositories_graphql_retries_gateway_failure_down_to_25() -> None:
251
+ client = _FakeClient(
252
+ _graphql_response(
253
+ _repository_page_data(
254
+ "R_1",
255
+ "repo-1",
256
+ repository_end_cursor="cursor-page-2",
257
+ repositories_has_next_page=True,
258
+ )
259
+ ),
260
+ _graphql_response(status_code=502, text="bad gateway"),
261
+ _graphql_response(status_code=504, text="gateway timeout"),
262
+ _graphql_response(_repository_page_data("R_2", "repo-2")),
263
+ )
264
+ ctx = SourceContext(
265
+ client=client,
266
+ organizations=[OrgContext(client=client, org_name="org")],
267
+ )
268
+
269
+ rows = list(repositories_graphql.__wrapped__(ctx))
270
+
271
+ assert [row["id"] for row in rows] == ["R_1", "R_2"]
272
+ assert _request_pages(client) == [
273
+ (None, 100),
274
+ ("cursor-page-2", 100),
275
+ ("cursor-page-2", 50),
276
+ ("cursor-page-2", 25),
277
+ ]
278
+
279
+
280
+ def test_repositories_graphql_logs_terminal_gateway_failure_at_smallest_page_size(
281
+ caplog,
282
+ ) -> None:
283
+ client = _FakeClient(
284
+ _graphql_response(
285
+ _repository_page_data(
286
+ "R_1",
287
+ "repo-1",
288
+ repository_end_cursor="cursor-page-2",
289
+ repositories_has_next_page=True,
290
+ )
291
+ ),
292
+ _graphql_response(status_code=502, text="bad gateway"),
293
+ _graphql_response(status_code=504, text="gateway timeout"),
294
+ _graphql_response(status_code=502, text="bad gateway"),
295
+ )
296
+ ctx = SourceContext(
297
+ client=client,
298
+ organizations=[OrgContext(client=client, org_name="org")],
299
+ )
300
+
301
+ with caplog.at_level(logging.ERROR, logger="openhound_github.resources.organization"):
302
+ rows = list(repositories_graphql.__wrapped__(ctx))
303
+
304
+ assert [row["id"] for row in rows] == ["R_1"]
305
+ assert _request_pages(client) == [
306
+ (None, 100),
307
+ ("cursor-page-2", 100),
308
+ ("cursor-page-2", 50),
309
+ ("cursor-page-2", 25),
310
+ ]
311
+ assert (
312
+ "at repository cursor 'cursor-page-2' with page size 25 "
313
+ "after emitting 1 repositories"
314
+ ) in caplog.text
315
+
316
+
317
+ def test_repositories_graphql_stays_degraded_after_failed_probe() -> None:
318
+ client = _FakeClient(
319
+ _graphql_response(
320
+ _repository_page_data(
321
+ "R_1",
322
+ "repo-1",
323
+ repository_end_cursor="cursor-page-2",
324
+ repositories_has_next_page=True,
325
+ )
326
+ ),
327
+ _graphql_response(status_code=502, text="bad gateway"),
328
+ _graphql_response(
329
+ _repository_page_data(
330
+ "R_2",
331
+ "repo-2",
332
+ repository_end_cursor="cursor-page-3",
333
+ repositories_has_next_page=True,
334
+ )
335
+ ),
336
+ _graphql_response(status_code=502, text="bad gateway"),
337
+ _graphql_response(
338
+ _repository_page_data(
339
+ "R_3",
340
+ "repo-3",
341
+ repository_end_cursor="cursor-page-4",
342
+ repositories_has_next_page=True,
343
+ )
344
+ ),
345
+ _graphql_response(_repository_page_data("R_4", "repo-4")),
346
+ )
347
+ ctx = SourceContext(
348
+ client=client,
349
+ organizations=[OrgContext(client=client, org_name="org")],
350
+ )
351
+
352
+ rows = list(repositories_graphql.__wrapped__(ctx))
353
+
354
+ assert [row["id"] for row in rows] == ["R_1", "R_2", "R_3", "R_4"]
355
+ assert _request_pages(client) == [
356
+ (None, 100),
357
+ ("cursor-page-2", 100),
358
+ ("cursor-page-2", 50),
359
+ ("cursor-page-3", 100),
360
+ ("cursor-page-3", 50),
361
+ ("cursor-page-4", 50),
362
+ ]
363
+
364
+
365
+ def test_repository_node_surfaces_branch_ruleset_presence() -> None:
366
+ repo = _make_repository()
367
+ lookup = MagicMock()
368
+ lookup.org_id_for_login.return_value = "O_1"
369
+ lookup.repository_branch_ruleset_count.return_value = 2
370
+ repo._lookup = lookup
371
+
372
+ node = repo.as_node
373
+
374
+ assert node.properties.branch_ruleset_count == 2
375
+ assert node.properties.has_branch_rulesets is True
376
+ assert node.properties.size == 0
377
+ lookup.repository_branch_ruleset_count.assert_called_once_with("R_1")
378
+
379
+
380
+ def test_repository_node_preserves_unknown_branch_ruleset_presence() -> None:
381
+ repo = _make_repository()
382
+ lookup = MagicMock()
383
+ lookup.org_id_for_login.return_value = "O_1"
384
+ lookup.repository_branch_ruleset_count.return_value = None
385
+ repo._lookup = lookup
386
+
387
+ node = repo.as_node
388
+
389
+ assert node.properties.branch_ruleset_count is None
390
+ assert node.properties.has_branch_rulesets is None
391
+
392
+
393
+ def test_repository_branch_ruleset_count_lookup_returns_int() -> None:
394
+ connection = duckdb.connect(":memory:")
395
+ connection.execute("CREATE SCHEMA github")
396
+ connection.execute(
397
+ "CREATE TABLE github.repositories_graphql (id VARCHAR, branch_ruleset_count BIGINT)"
398
+ )
399
+ connection.execute(
400
+ "INSERT INTO github.repositories_graphql VALUES ('R_1', 2), ('R_2', NULL)"
401
+ )
402
+
403
+ lookup = GithubLookup(connection)
404
+
405
+ assert lookup.repository_branch_ruleset_count("R_1") == 2
406
+ assert lookup.repository_branch_ruleset_count("R_2") is None