openhound-github 0.22.0__tar.gz → 0.22.1__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 (456) hide show
  1. {openhound_github-0.22.0 → openhound_github-0.22.1}/PKG-INFO +22 -1
  2. {openhound_github-0.22.0 → openhound_github-0.22.1}/README.md +21 -0
  3. openhound_github-0.22.1/src/openhound_github/github_rest_client.py +90 -0
  4. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/helpers.py +15 -8
  5. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/resources/organization.py +28 -5
  6. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/source.py +2 -5
  7. openhound_github-0.22.1/tests/test_collection_continuation.py +153 -0
  8. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_github_app_retry.py +36 -0
  9. openhound_github-0.22.1/tests/test_github_rest_client.py +161 -0
  10. openhound_github-0.22.1/tests/test_graphql_retry_budget.py +100 -0
  11. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_workflow_resources.py +61 -2
  12. openhound_github-0.22.0/src/openhound_github/github_rest_client.py +0 -3
  13. openhound_github-0.22.0/tests/test_github_rest_client.py +0 -52
  14. {openhound_github-0.22.0 → openhound_github-0.22.1}/.dockerignore +0 -0
  15. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/CODE_OF_CONDUCT.md +0 -0
  16. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/CONTRIBUTING.md +0 -0
  17. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/GOVERNANCE.md +0 -0
  18. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/SECURITY.md +0 -0
  19. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/SO-Dark.svg +0 -0
  20. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/SO-FOSS-Dark.svg +0 -0
  21. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/SO-FOSS-Light.svg +0 -0
  22. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/SO-Light.svg +0 -0
  23. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/workflows/publish-container.yml +0 -0
  24. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/workflows/publish.yml +0 -0
  25. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/workflows/release-on-merge.yml +0 -0
  26. {openhound_github-0.22.0 → openhound_github-0.22.1}/.github/workflows/validate-branch.yml +0 -0
  27. {openhound_github-0.22.0 → openhound_github-0.22.1}/.gitignore +0 -0
  28. {openhound_github-0.22.0 → openhound_github-0.22.1}/.pre-commit-config.yaml +0 -0
  29. {openhound_github-0.22.0 → openhound_github-0.22.1}/Dockerfile +0 -0
  30. {openhound_github-0.22.0 → openhound_github-0.22.1}/LICENSE.md +0 -0
  31. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AddAssignee.md +0 -0
  32. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AddCollaborator.md +0 -0
  33. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AddLabel.md +0 -0
  34. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AddMember.md +0 -0
  35. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AddedDeployKey.md +0 -0
  36. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AdminTo.md +0 -0
  37. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ApprovesDeploymentTo.md +0 -0
  38. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AssignedTo.md +0 -0
  39. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_AuthorizedForOrganization.md +0 -0
  40. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_BypassBranchProtection.md +0 -0
  41. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_BypassPullRequestAllowances.md +0 -0
  42. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CallsWorkflow.md +0 -0
  43. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanAccess.md +0 -0
  44. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanAccessSecret.md +0 -0
  45. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanAssumeIdentity.md +0 -0
  46. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreateBranch.md +0 -0
  47. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreateEnvironment.md +0 -0
  48. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreateInternalRepositories.md +0 -0
  49. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreatePrivateRepositories.md +0 -0
  50. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreatePublicRepositories.md +0 -0
  51. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreateRepositories.md +0 -0
  52. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanCreateRepositoryWithRunnerAccess.md +0 -0
  53. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanDeployToEnvironment.md +0 -0
  54. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanEditEnvironment.md +0 -0
  55. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanEditProtection.md +0 -0
  56. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanInterceptJob.md +0 -0
  57. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanPwnRequest.md +0 -0
  58. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanReadSecret.md +0 -0
  59. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanReadSecretScanningAlert.md +0 -0
  60. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanRequestOIDCTokenFor.md +0 -0
  61. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanUseRunner.md +0 -0
  62. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CanWriteBranch.md +0 -0
  63. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CloseDiscussion.md +0 -0
  64. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CloseIssue.md +0 -0
  65. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ClosePullRequest.md +0 -0
  66. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_Contains.md +0 -0
  67. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ConvertIssuesToDiscussions.md +0 -0
  68. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CreateDiscussionCategory.md +0 -0
  69. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CreateEnterpriseOrganizations.md +0 -0
  70. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CreateRepository.md +0 -0
  71. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CreateSoloMergeQueueEntry.md +0 -0
  72. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CreateTag.md +0 -0
  73. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_CreateTeam.md +0 -0
  74. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DeleteAlertsCodeScanning.md +0 -0
  75. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DeleteDiscussion.md +0 -0
  76. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DeleteDiscussionComment.md +0 -0
  77. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DeleteIssue.md +0 -0
  78. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DeleteTag.md +0 -0
  79. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DependsOn.md +0 -0
  80. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_DeploysTo.md +0 -0
  81. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditCategoryOnDiscussion.md +0 -0
  82. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditDiscussionCategory.md +0 -0
  83. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditDiscussionComment.md +0 -0
  84. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditEnterpriseCustomPropertiesForOrganizations.md +0 -0
  85. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditRepoAnnouncementBanners.md +0 -0
  86. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditRepoCustomPropertiesValues.md +0 -0
  87. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditRepoMetadata.md +0 -0
  88. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_EditRepoProtections.md +0 -0
  89. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasBaseRole.md +0 -0
  90. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasExternalIdentity.md +0 -0
  91. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasMember.md +0 -0
  92. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasPersonalAccessToken.md +0 -0
  93. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasPersonalAccessTokenRequest.md +0 -0
  94. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasRole.md +0 -0
  95. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasRunner.md +0 -0
  96. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasSamlIdentityProvider.md +0 -0
  97. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasSecret.md +0 -0
  98. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_HasVariable.md +0 -0
  99. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_InheritedFrom.md +0 -0
  100. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_InstalledAs.md +0 -0
  101. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_InviteMember.md +0 -0
  102. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_IsEligibleFor.md +0 -0
  103. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_JumpMergeQueue.md +0 -0
  104. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageDeployKeys.md +0 -0
  105. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageDiscussionBadges.md +0 -0
  106. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseAdmins.md +0 -0
  107. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseIdentityProvider.md +0 -0
  108. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseMembers.md +0 -0
  109. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseOrganizationAdmins.md +0 -0
  110. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseOrganizations.md +0 -0
  111. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseReferrals.md +0 -0
  112. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageEnterpriseTeams.md +0 -0
  113. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageOrganizationWebhooks.md +0 -0
  114. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageRepoSecurityProducts.md +0 -0
  115. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageSecurityProducts.md +0 -0
  116. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageSettingsMergeTypes.md +0 -0
  117. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageSettingsPages.md +0 -0
  118. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageSettingsProjects.md +0 -0
  119. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageSettingsWiki.md +0 -0
  120. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageTopics.md +0 -0
  121. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ManageWebhooks.md +0 -0
  122. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_MapsToUser.md +0 -0
  123. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_MarkAsDuplicate.md +0 -0
  124. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_MatchesEnvironmentPolicy.md +0 -0
  125. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_MemberOf.md +0 -0
  126. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_OrgBypassCodeScanningDismissalRequests.md +0 -0
  127. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_OrgBypassSecretScanningClosureRequests.md +0 -0
  128. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_OrgReviewAndManageSecretScanningBypassRequests.md +0 -0
  129. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_OrgReviewAndManageSecretScanningClosureRequests.md +0 -0
  130. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_Owns.md +0 -0
  131. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ProtectedBy.md +0 -0
  132. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_PushProtectedBranch.md +0 -0
  133. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadCodeScanning.md +0 -0
  134. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadEnterpriseAuditLog.md +0 -0
  135. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadEnterpriseDomainVerification.md +0 -0
  136. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadEnterpriseMembers.md +0 -0
  137. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadEnterpriseOrgProjects.md +0 -0
  138. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadEnterpriseOrganizationAdmin.md +0 -0
  139. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadOrganizationActionsUsageMetrics.md +0 -0
  140. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadOrganizationCustomOrgRole.md +0 -0
  141. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadOrganizationCustomRepoRole.md +0 -0
  142. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReadRepoContents.md +0 -0
  143. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RemoveAssignee.md +0 -0
  144. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RemoveLabel.md +0 -0
  145. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReopenDiscussion.md +0 -0
  146. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReopenIssue.md +0 -0
  147. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ReopenPullRequest.md +0 -0
  148. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RequestPrReview.md +0 -0
  149. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RequestsAccessTo.md +0 -0
  150. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ResolveDependabotAlerts.md +0 -0
  151. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ResolveSecretScanningAlerts.md +0 -0
  152. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RestrictionsCanPush.md +0 -0
  153. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RunOrgMigration.md +0 -0
  154. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_RunsOn.md +0 -0
  155. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ScopedTo.md +0 -0
  156. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_SetEnterpriseInteractionLimits.md +0 -0
  157. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_SetInteractionLimits.md +0 -0
  158. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_SetIssueType.md +0 -0
  159. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_SetMilestone.md +0 -0
  160. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_SetSocialPreview.md +0 -0
  161. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_SyncedTo.md +0 -0
  162. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ToggleDiscussionAnswer.md +0 -0
  163. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ToggleDiscussionCommentMinimize.md +0 -0
  164. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_TransferRepository.md +0 -0
  165. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_UsesSecret.md +0 -0
  166. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_UsesVariable.md +0 -0
  167. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ValidToken.md +0 -0
  168. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ViewDependabotAlerts.md +0 -0
  169. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ViewEnterpriseActionsUsageMetrics.md +0 -0
  170. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ViewEnterpriseBilling.md +0 -0
  171. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ViewEnterpriseSecretScanningAlerts.md +0 -0
  172. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_ViewSecretScanningAlerts.md +0 -0
  173. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteCodeScanning.md +0 -0
  174. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteEnterpriseActionsPolicies.md +0 -0
  175. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteEnterpriseBilling.md +0 -0
  176. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteEnterprisePersonalAccessTokenPolicies.md +0 -0
  177. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteEnterpriseSso.md +0 -0
  178. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteEnterpriseTeamMembers.md +0 -0
  179. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteOrganizationActionsSecrets.md +0 -0
  180. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteOrganizationActionsSettings.md +0 -0
  181. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteOrganizationActionsVariables.md +0 -0
  182. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteOrganizationCustomOrgRole.md +0 -0
  183. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteOrganizationCustomRepoRole.md +0 -0
  184. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteOrganizationNetworkConfigurations.md +0 -0
  185. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteRepoContents.md +0 -0
  186. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/edges/GH_WriteRepoPullRequests.md +0 -0
  187. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_App.md +0 -0
  188. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_AppInstallation.md +0 -0
  189. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Branch.md +0 -0
  190. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_BranchProtectionRule.md +0 -0
  191. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_ClassicPersonalAccessToken.md +0 -0
  192. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_DeployKey.md +0 -0
  193. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Enterprise.md +0 -0
  194. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnterpriseManagedUser.md +0 -0
  195. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnterpriseRole.md +0 -0
  196. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnterpriseRunner.md +0 -0
  197. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnterpriseRunnerGroup.md +0 -0
  198. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnterpriseTeam.md +0 -0
  199. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Environment.md +0 -0
  200. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnvironmentBranchPolicy.md +0 -0
  201. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnvironmentSecret.md +0 -0
  202. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_EnvironmentVariable.md +0 -0
  203. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_ExternalIdentity.md +0 -0
  204. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_OrgRole.md +0 -0
  205. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_OrgRunner.md +0 -0
  206. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_OrgRunnerGroup.md +0 -0
  207. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_OrgSecret.md +0 -0
  208. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_OrgVariable.md +0 -0
  209. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Organization.md +0 -0
  210. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_PersonalAccessToken.md +0 -0
  211. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_PersonalAccessTokenRequest.md +0 -0
  212. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_RepoRole.md +0 -0
  213. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_RepoRunner.md +0 -0
  214. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_RepoSecret.md +0 -0
  215. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_RepoVariable.md +0 -0
  216. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Repository.md +0 -0
  217. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Runner.md +0 -0
  218. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_RunnerGroup.md +0 -0
  219. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_SamlIdentityProvider.md +0 -0
  220. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Scope.md +0 -0
  221. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Secret.md +0 -0
  222. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_SecretScanningAlert.md +0 -0
  223. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Team.md +0 -0
  224. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_TeamRole.md +0 -0
  225. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_User.md +0 -0
  226. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Variable.md +0 -0
  227. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_Workflow.md +0 -0
  228. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_WorkflowJob.md +0 -0
  229. {openhound_github-0.22.0 → openhound_github-0.22.1}/descriptions/nodes/GH_WorkflowStep.md +0 -0
  230. {openhound_github-0.22.0 → openhound_github-0.22.1}/docker-compose/.dlt/config.toml.example +0 -0
  231. {openhound_github-0.22.0 → openhound_github-0.22.1}/docker-compose/.dlt/secrets.toml.example +0 -0
  232. {openhound_github-0.22.0 → openhound_github-0.22.1}/docker-compose/.env.example +0 -0
  233. {openhound_github-0.22.0 → openhound_github-0.22.1}/docker-compose/README.md +0 -0
  234. {openhound_github-0.22.0 → openhound_github-0.22.1}/docker-compose/check_config.py +0 -0
  235. {openhound_github-0.22.0 → openhound_github-0.22.1}/docker-compose/compose.yaml +0 -0
  236. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-all-repo-admin-role.json +0 -0
  237. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-app-installations-all-repos.json +0 -0
  238. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-apps-all-repos.json +0 -0
  239. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-enterprise-owners-role.json +0 -0
  240. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-external-identities-owners.json +0 -0
  241. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-organizations.json +0 -0
  242. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-owner-users.json +0 -0
  243. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-owners-role.json +0 -0
  244. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-pats-all-repos.json +0 -0
  245. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-privilege-escalation-roles.json +0 -0
  246. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-privilege-escalation-users.json +0 -0
  247. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/privilege_zone_rules/t0-saml-identity-providers.json +0 -0
  248. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/README.md +0 -0
  249. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/actions-sha-pinning-not-required.json +0 -0
  250. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/active-leaked-secrets.json +0 -0
  251. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/advanced-security-disabled-new-repos.json +0 -0
  252. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/all-actions-allowed.json +0 -0
  253. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/app-installations-all-repos.json +0 -0
  254. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-admins-not-enforced.json +0 -0
  255. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-deletions-allowed.json +0 -0
  256. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-force-pushes.json +0 -0
  257. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-no-code-owner-reviews.json +0 -0
  258. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-no-pr-reviews.json +0 -0
  259. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-no-status-checks.json +0 -0
  260. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-self-approval.json +0 -0
  261. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/branch-protection-stale-reviews.json +0 -0
  262. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/bypass-pr-requirements.json +0 -0
  263. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/dangerous-branch-perms.json +0 -0
  264. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/default-repository-permissions.json +0 -0
  265. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/demo-sso-to-cloud-round-trip.json +0 -0
  266. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/dependabot-alerts-disabled-new-repos.json +0 -0
  267. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/dependabot-updates-disabled-new-repos.json +0 -0
  268. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/dependency-graph-disabled-new-repos.json +0 -0
  269. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/environments-admin-bypass.json +0 -0
  270. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/expired-pats.json +0 -0
  271. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/external-identities-without-scim.json +0 -0
  272. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/github-to-azure-identity.json +0 -0
  273. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/global-repo-perms.json +0 -0
  274. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/hybrid-identities.json +0 -0
  275. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/members-can-change-repo-visibility.json +0 -0
  276. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/members-can-create-pages.json +0 -0
  277. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/members-can-create-public-repos.json +0 -0
  278. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/members-can-delete-repos.json +0 -0
  279. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/members-can-fork-private-repos.json +0 -0
  280. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/members-can-invite-outside-collaborators.json +0 -0
  281. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/org-owners.json +0 -0
  282. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/orgs-without-2fa.json +0 -0
  283. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/pats-all-repo-access.json +0 -0
  284. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/pending-pat-requests.json +0 -0
  285. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/private-repos-forking-allowed.json +0 -0
  286. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/privileged-custom-org-roles.json +0 -0
  287. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/privileged-hybrid-identities.json +0 -0
  288. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/public-repos.json +0 -0
  289. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/push-protection-disabled-new-repos.json +0 -0
  290. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/push-to-protected-branches.json +0 -0
  291. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/repos-secret-scanning-disabled.json +0 -0
  292. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/repos-vulnerable-to-workflow-secret-exfil.json +0 -0
  293. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/repository-workflows.json +0 -0
  294. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/saml-configuration.json +0 -0
  295. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/secret-scanning-alerts.json +0 -0
  296. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/secret-scanning-disabled-new-repos.json +0 -0
  297. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/secrets-reachable-by-user.json +0 -0
  298. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/self-hosted-runners-can-intercept-broad-token-jobs.json +0 -0
  299. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/shared-self-hosted-runners-can-intercept-secret-bearing-jobs.json +0 -0
  300. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/team-membership-admin.json +0 -0
  301. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/team-structure.json +0 -0
  302. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/unprotected-branches.json +0 -0
  303. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/unprotected-default-branch-with-workflow.json +0 -0
  304. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/unprotected-default-branches.json +0 -0
  305. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/web-commit-signoff-not-required.json +0 -0
  306. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/workflow-jobs-interceptable-by-self-hosted-runners.json +0 -0
  307. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/workflow-jobs-with-broad-token-write-permissions.json +0 -0
  308. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/workflow-jobs-with-id-token-write-on-self-hosted-runners.json +0 -0
  309. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/workflow-jobs-with-id-token-write.json +0 -0
  310. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/saved_searches/workflow-jobs-with-observed-oidc-auth-steps.json +0 -0
  311. {openhound_github-0.22.0 → openhound_github-0.22.1}/extension/schema.json +0 -0
  312. {openhound_github-0.22.0 → openhound_github-0.22.1}/justfile +0 -0
  313. {openhound_github-0.22.0 → openhound_github-0.22.1}/pyproject.toml +0 -0
  314. {openhound_github-0.22.0 → openhound_github-0.22.1}/scripts/generate_gh_description_docs.py +0 -0
  315. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/main.py +0 -0
  316. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/__init__.py +0 -0
  317. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/auth.py +0 -0
  318. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/extension.yaml +0 -0
  319. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/github_retry.py +0 -0
  320. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/graph.py +0 -0
  321. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/graphql.py +0 -0
  322. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/kinds/edges.py +0 -0
  323. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/kinds/nodes.py +0 -0
  324. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/lookup.py +0 -0
  325. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/main.py +0 -0
  326. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/__init__.py +0 -0
  327. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/actions_permission.py +0 -0
  328. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/app_installation.py +0 -0
  329. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/branch.py +0 -0
  330. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/branch_pr_bypass_allowance.py +0 -0
  331. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/branch_protection_rule.py +0 -0
  332. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/branch_push_allowance.py +0 -0
  333. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/classic_personal_access_token.py +0 -0
  334. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/deploy_key.py +0 -0
  335. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise.py +0 -0
  336. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_admin.py +0 -0
  337. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_credential_inventory.py +0 -0
  338. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_helpers.py +0 -0
  339. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_managed_user.py +0 -0
  340. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_member.py +0 -0
  341. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_organization.py +0 -0
  342. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_role.py +0 -0
  343. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_role_team.py +0 -0
  344. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_role_user.py +0 -0
  345. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_team.py +0 -0
  346. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_team_member.py +0 -0
  347. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_team_organization.py +0 -0
  348. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_team_role.py +0 -0
  349. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/enterprise_user.py +0 -0
  350. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/env_secret.py +0 -0
  351. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/env_variable.py +0 -0
  352. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/environment.py +0 -0
  353. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/environment_branch_policy.py +0 -0
  354. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/external_identity.py +0 -0
  355. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/org.py +0 -0
  356. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/org_role.py +0 -0
  357. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/org_role_member.py +0 -0
  358. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/org_role_team.py +0 -0
  359. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/org_secret.py +0 -0
  360. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/org_variable.py +0 -0
  361. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/permissions.py +0 -0
  362. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/personal_access_token.py +0 -0
  363. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/personal_access_token_access.py +0 -0
  364. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/personal_access_token_request.py +0 -0
  365. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/projected_enterprise_team.py +0 -0
  366. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/repo_role_assignment.py +0 -0
  367. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/repository.py +0 -0
  368. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/repository_role.py +0 -0
  369. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/repository_secret.py +0 -0
  370. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/repository_variable.py +0 -0
  371. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/runner.py +0 -0
  372. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/saml_assertion_consumer_service.py +0 -0
  373. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/saml_helpers.py +0 -0
  374. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/saml_issuer.py +0 -0
  375. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/saml_provider.py +0 -0
  376. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/saml_service_provider.py +0 -0
  377. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/scim_user.py +0 -0
  378. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/scope.py +0 -0
  379. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/secret_scanning_alert.py +0 -0
  380. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/team.py +0 -0
  381. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/team_member.py +0 -0
  382. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/team_role.py +0 -0
  383. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/user.py +0 -0
  384. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/workflow.py +0 -0
  385. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/workflow_job.py +0 -0
  386. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/workflow_reference.py +0 -0
  387. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/models/workflow_step.py +0 -0
  388. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/resources/__init__.py +0 -0
  389. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/resources/enterprise.py +0 -0
  390. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/runner_ids.py +0 -0
  391. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/openhound_github/transforms.py +0 -0
  392. {openhound_github-0.22.0 → openhound_github-0.22.1}/src/scheduler.py +0 -0
  393. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_app_auth.py +0 -0
  394. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_app_installation_repo_access.py +0 -0
  395. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_app_model.py +0 -0
  396. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_branch_collection_gate.py +0 -0
  397. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_branch_protection_traversability.py +0 -0
  398. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_classic_personal_access_tokens.py +0 -0
  399. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_credential_permission_models.py +0 -0
  400. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/actions-permissions.json +0 -0
  401. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/actionsecrets.json +0 -0
  402. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/app.json +0 -0
  403. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/appinstalls.json +0 -0
  404. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/branch-policies.json +0 -0
  405. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/branch-prot-rules.json +0 -0
  406. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/branches.json +0 -0
  407. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/custom-roles.json +0 -0
  408. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/environments.json +0 -0
  409. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/org-pat-request.json +0 -0
  410. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/org-pat.json +0 -0
  411. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/org-role-teams.json +0 -0
  412. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/org-role-users.json +0 -0
  413. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/org-roles.json +0 -0
  414. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/org.json +0 -0
  415. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/pat-repositories.json +0 -0
  416. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/repo-collaborators.json +0 -0
  417. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/repo-teams.json +0 -0
  418. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/repositories.json +0 -0
  419. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/scimusers.json +0 -0
  420. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/secrets.json +0 -0
  421. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/secretscanningalert.json +0 -0
  422. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/selected-org-secrets.json +0 -0
  423. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/variables.json +0 -0
  424. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_data/api/workflows.json +0 -0
  425. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_deploy_key_models.py +0 -0
  426. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_deploy_key_resources.py +0 -0
  427. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_description_docs.py +0 -0
  428. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_enterprise_capabilities.py +0 -0
  429. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_enterprise_organization.py +0 -0
  430. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_enterprise_resources.py +0 -0
  431. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_environment_model.py +0 -0
  432. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_environment_resources.py +0 -0
  433. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_environment_reviewer_lookup.py +0 -0
  434. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_error_resilience.py +0 -0
  435. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_github_retry.py +0 -0
  436. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_helpers.py +0 -0
  437. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_lookup.py +0 -0
  438. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_org_role_model.py +0 -0
  439. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_org_scim_resources.py +0 -0
  440. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_org_secret_models.py +0 -0
  441. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_preproc.py +0 -0
  442. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_repository_rulesets.py +0 -0
  443. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_repository_scopes.py +0 -0
  444. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_runner_ids.py +0 -0
  445. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_runner_models.py +0 -0
  446. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_runner_resources.py +0 -0
  447. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_saml_helpers.py +0 -0
  448. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_saml_models.py +0 -0
  449. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_scim_models.py +0 -0
  450. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_source_endpoints.py +0 -0
  451. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_source_org_canonicalization.py +0 -0
  452. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_team_external_groups.py +0 -0
  453. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_variable_models.py +0 -0
  454. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_workflow_interception_path.py +0 -0
  455. {openhound_github-0.22.0 → openhound_github-0.22.1}/tests/test_workflow_model.py +0 -0
  456. {openhound_github-0.22.0 → openhound_github-0.22.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openhound-github
3
- Version: 0.22.0
3
+ Version: 0.22.1
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
@@ -66,6 +66,27 @@ Both values must be provided together when overriding the GitHub.com defaults.
66
66
  Endpoint URLs must use HTTPS and share the same origin. GitHub App
67
67
  `credentials.api_uri` may override only the path on that same origin.
68
68
 
69
+ ### Request timeouts and retries
70
+
71
+ REST and GraphQL collection requests use a 10-second connection timeout and a
72
+ 30-second read timeout. Transient HTTP 5xx errors, connection failures, and
73
+ timeouts share a limit of three attempts (the initial attempt plus two retries).
74
+ Adaptive GraphQL pagination shares that limit across page sizes: HTTP 502/504
75
+ responses trigger smaller-page attempts instead of a new retry budget.
76
+
77
+ After retries are exhausted, existing resource handlers log the failure and
78
+ continue with unaffected scopes. Already emitted records are retained, but the
79
+ affected scope may be incomplete. Resources that require all pages before
80
+ emitting records retain that behavior.
81
+
82
+ Workflow collection skips `dynamic/` paths. Missing workflow files are logged
83
+ and skipped without passing failed items to job or step parsing.
84
+
85
+ Rate-limit responses and GitHub App token refresh retain their existing retry
86
+ handling and do not consume the transient-failure budget. These timeouts are
87
+ not an overall request deadline. App authentication requests and credential
88
+ inventory CSV downloads retain their separate timeout settings.
89
+
69
90
  ### Enterprise SCIM and hybrid correlations
70
91
 
71
92
  A token with enterprise SCIM access is used to collect both `/scim/v2/enterprises/{enterprise}/Users` and `/scim/v2/enterprises/{enterprise}/Groups`. The collector emits normalized `SCIM_Organization`, `SCIM_User`, and `SCIM_Group` nodes plus `SCIM_Contains`, `SCIM_MemberOf`, and `SCIM_Provisioned` relationships. Install the BloodHound SCIM extension alongside this extension to register the shared SCIM kinds.
@@ -55,6 +55,27 @@ Both values must be provided together when overriding the GitHub.com defaults.
55
55
  Endpoint URLs must use HTTPS and share the same origin. GitHub App
56
56
  `credentials.api_uri` may override only the path on that same origin.
57
57
 
58
+ ### Request timeouts and retries
59
+
60
+ REST and GraphQL collection requests use a 10-second connection timeout and a
61
+ 30-second read timeout. Transient HTTP 5xx errors, connection failures, and
62
+ timeouts share a limit of three attempts (the initial attempt plus two retries).
63
+ Adaptive GraphQL pagination shares that limit across page sizes: HTTP 502/504
64
+ responses trigger smaller-page attempts instead of a new retry budget.
65
+
66
+ After retries are exhausted, existing resource handlers log the failure and
67
+ continue with unaffected scopes. Already emitted records are retained, but the
68
+ affected scope may be incomplete. Resources that require all pages before
69
+ emitting records retain that behavior.
70
+
71
+ Workflow collection skips `dynamic/` paths. Missing workflow files are logged
72
+ and skipped without passing failed items to job or step parsing.
73
+
74
+ Rate-limit responses and GitHub App token refresh retain their existing retry
75
+ handling and do not consume the transient-failure budget. These timeouts are
76
+ not an overall request deadline. App authentication requests and credential
77
+ inventory CSV downloads retain their separate timeout settings.
78
+
58
79
  ### Enterprise SCIM and hybrid correlations
59
80
 
60
81
  A token with enterprise SCIM access is used to collect both `/scim/v2/enterprises/{enterprise}/Users` and `/scim/v2/enterprises/{enterprise}/Groups`. The collector emits normalized `SCIM_Organization`, `SCIM_User`, and `SCIM_Group` nodes plus `SCIM_Contains`, `SCIM_MemberOf`, and `SCIM_Provisioned` relationships. Install the BloodHound SCIM extension alongside this extension to register the shared SCIM kinds.
@@ -0,0 +1,90 @@
1
+ from contextlib import contextmanager
2
+ from contextvars import ContextVar
3
+ from dataclasses import dataclass
4
+ from functools import wraps
5
+
6
+ from dlt.sources.helpers import requests
7
+ from requests.exceptions import ChunkedEncodingError, ConnectionError, Timeout
8
+ from tenacity import RetryCallState
9
+ from tenacity.stop import stop_base
10
+
11
+ from openhound_github.github_retry import should_retry_github_response
12
+
13
+ __all__ = ["github_http_session", "should_retry_github_response"]
14
+
15
+
16
+ @dataclass
17
+ class GraphQLPageRetryBudget:
18
+ """Share transient failures across page sizes without changing other requests."""
19
+
20
+ failures: int = 0
21
+
22
+ @property
23
+ def exhausted(self) -> bool:
24
+ return self.failures >= 3
25
+
26
+ @contextmanager
27
+ def activate(self):
28
+ token = _graphql_page_budget.set(self)
29
+ try:
30
+ yield
31
+ finally:
32
+ _graphql_page_budget.reset(token)
33
+
34
+
35
+ _graphql_page_budget: ContextVar[GraphQLPageRetryBudget | None] = ContextVar(
36
+ "github_graphql_page_budget", default=None
37
+ )
38
+
39
+
40
+ class _StopAfterTransientFailures(stop_base):
41
+ """Count transient failures per send, independently of other retry reasons."""
42
+
43
+ def __call__(self, retry_state: RetryCallState) -> bool:
44
+ outcome = retry_state.outcome
45
+ if outcome is None:
46
+ return False
47
+ exception = outcome.exception()
48
+ response = None
49
+ if isinstance(exception, requests.HTTPError):
50
+ response = exception.response
51
+ elif exception is None:
52
+ response = outcome.result()
53
+ transient = isinstance(exception, (ConnectionError, Timeout, ChunkedEncodingError)) or (
54
+ response is not None and 500 <= response.status_code < 600
55
+ )
56
+ page_budget = _graphql_page_budget.get()
57
+ if page_budget is not None:
58
+ page_budget.failures += int(transient)
59
+ # Return gateway errors immediately so pagination can shrink the page.
60
+ return page_budget.exhausted or (
61
+ response is not None and response.status_code in (502, 504)
62
+ )
63
+ failures = getattr(retry_state, "_github_transient_failures", 0) + int(transient)
64
+ setattr(retry_state, "_github_transient_failures", failures)
65
+ return failures >= 3
66
+
67
+
68
+ def github_http_session(retry_condition):
69
+ """Bound transient failures while retaining DLT's existing retry policy."""
70
+ session = requests.Client(
71
+ request_timeout=(10, 30),
72
+ status_codes=tuple(range(500, 600)),
73
+ retry_condition=retry_condition,
74
+ ).session
75
+ # Keep the configured overall limit for rate limits and credential refresh.
76
+ # RetryCallState is new for every send, including concurrent requests.
77
+ retry = session.send.retry
78
+ retry.stop = _StopAfterTransientFailures() | retry.stop
79
+ send = session.send
80
+
81
+ @wraps(send)
82
+ def send_with_status_check(request, **kwargs):
83
+ response = send(request, **kwargs)
84
+ # RESTClient calls send directly, bypassing Session.request's status check.
85
+ # Raise only after the retry loop finishes so scope handlers can skip it.
86
+ response.raise_for_status()
87
+ return response
88
+
89
+ session.send = send_with_status_check
90
+ return session
@@ -14,6 +14,7 @@ from dlt.sources.helpers.rest_client.paginators import (
14
14
  from requests import Request
15
15
 
16
16
  from openhound_github.auth import GitHubAppInstallationAuth
17
+ from openhound_github.github_rest_client import GraphQLPageRetryBudget
17
18
  from openhound_github.github_retry import (
18
19
  is_primary_rate_limit_response,
19
20
  is_secondary_rate_limit_response,
@@ -230,9 +231,9 @@ def adaptive_graphql_paginate(
230
231
  ) -> Iterator[dict[str, Any]]:
231
232
  """Yield GraphQL pages while adapting connection size after gateway failures.
232
233
 
233
- The underlying HTTP session still owns ordinary request retries. This helper
234
- handles the remaining terminal 502/504 case by retrying the same cursor with
235
- progressively smaller connection sizes.
234
+ HTTP retries and smaller-page recovery share three transient attempts per
235
+ page. Gateway errors return immediately so the next attempt can shrink the
236
+ page; rate-limit and credential-refresh retries retain their existing policy.
236
237
  """
237
238
  if not page_sizes or any(page_size <= 0 for page_size in page_sizes):
238
239
  raise ValueError("page_sizes must contain positive integers")
@@ -270,6 +271,7 @@ def adaptive_graphql_paginate(
270
271
  page_data: dict[str, Any] | None = None
271
272
  paginator: GraphQLCursorPaginator | None = None
272
273
  successful_page_size: int | None = None
274
+ retry_budget = GraphQLPageRetryBudget()
273
275
 
274
276
  for index, page_size in enumerate(candidate_sizes):
275
277
  request_variables = {
@@ -278,10 +280,11 @@ def adaptive_graphql_paginate(
278
280
  page_size_variable: page_size,
279
281
  }
280
282
  try:
281
- response = client.post(
282
- graphql_path,
283
- json={"query": query, "variables": request_variables},
284
- )
283
+ with retry_budget.activate():
284
+ response = client.post(
285
+ graphql_path,
286
+ json={"query": query, "variables": request_variables},
287
+ )
285
288
  response.raise_for_status()
286
289
  response_json = response.json()
287
290
  paginator = GraphQLCursorPaginator(
@@ -303,7 +306,11 @@ def adaptive_graphql_paginate(
303
306
  status_code = _graphql_gateway_status(error)
304
307
  next_page_size = (
305
308
  candidate_sizes[index + 1]
306
- if status_code is not None and index + 1 < len(candidate_sizes)
309
+ if (
310
+ status_code is not None
311
+ and index + 1 < len(candidate_sizes)
312
+ and not retry_budget.exhausted
313
+ )
307
314
  else None
308
315
  )
309
316
  if next_page_size is not None:
@@ -6,6 +6,7 @@ from threading import Lock
6
6
  from typing import Any, Iterator
7
7
  from urllib.parse import quote
8
8
 
9
+ import dlt
9
10
  from dlt.sources.helpers import requests
10
11
  from dlt.sources.helpers.rest_client.client import RESTClient
11
12
  from dlt.sources.helpers.rest_client.paginators import (
@@ -1475,7 +1476,7 @@ def workflows(repo: Repository, ctx: SourceContext):
1475
1476
  Workflow (Workflow): An active workflow record.
1476
1477
  """
1477
1478
 
1478
- @app.defer
1479
+ @dlt.defer
1479
1480
  def _workflow_file_contents(
1480
1481
  client: RESTClient,
1481
1482
  repo: Repository,
@@ -1487,11 +1488,30 @@ def workflows(repo: Repository, ctx: SourceContext):
1487
1488
  return None
1488
1489
 
1489
1490
  params = {"ref": repo.default_branch} if repo.default_branch else None
1490
- response = client.get(
1491
- f"/repos/{repo.full_name}/contents/{path}", params=params
1492
- ).json()
1491
+ try:
1492
+ response = client.get(
1493
+ f"/repos/{repo.full_name}/contents/{path}", params=params
1494
+ ).json()
1495
+ content = response.get("content")
1496
+ except Exception as exc:
1497
+ # Return None so DLT drops the failed item. app.defer returns [],
1498
+ # which can reach the job/step transformers as an invalid Workflow.
1499
+ if (
1500
+ isinstance(exc, requests.HTTPError)
1501
+ and exc.response is not None
1502
+ and exc.response.status_code == 404
1503
+ ):
1504
+ logger.warning(
1505
+ "Skipping workflow '%s' in repository '%s': file unavailable at ref '%s' (HTTP 404)",
1506
+ path, repo.full_name, repo.default_branch,
1507
+ )
1508
+ else:
1509
+ logger.error(
1510
+ "Unable to collect workflow '%s' in repository '%s': %s",
1511
+ path, repo.full_name, exc,
1512
+ )
1513
+ return None
1493
1514
 
1494
- content = response.get("content")
1495
1515
  if not content:
1496
1516
  return None
1497
1517
 
@@ -1517,6 +1537,9 @@ def workflows(repo: Repository, ctx: SourceContext):
1517
1537
  ):
1518
1538
  for workflow in page:
1519
1539
  if workflow.get("state") == "active":
1540
+ path = workflow.get("path")
1541
+ if not path or path.startswith("dynamic/"):
1542
+ continue
1520
1543
  if workflow_permissions is None:
1521
1544
  workflow_permissions = _repository_workflow_permissions(
1522
1545
  ctx, client, repo.full_name
@@ -8,7 +8,6 @@ from urllib.parse import urlparse
8
8
  import dlt
9
9
  from dlt.common.configuration import configspec
10
10
  from dlt.common.configuration.specs import CredentialsConfiguration
11
- from dlt.sources.helpers import requests
12
11
  from dlt.sources.helpers.rest_client.auth import AuthConfigBase
13
12
  from dlt.sources.helpers.rest_client.auth import BearerTokenAuth
14
13
  from dlt.sources.helpers.rest_client.client import RESTClient
@@ -27,6 +26,7 @@ from openhound_github.helpers import (
27
26
  DEFAULT_GITHUB_REST_API_URL,
28
27
  github_retry_policy,
29
28
  )
29
+ from openhound_github.github_rest_client import github_http_session
30
30
  from openhound_github.main import app
31
31
  from openhound_github.models.saml_helpers import (
32
32
  DEFAULT_GITHUB_DEPLOYMENT_ID,
@@ -352,10 +352,7 @@ def source(
352
352
  },
353
353
  auth=auth,
354
354
  paginator=HeaderLinkPaginator(),
355
- session=requests.Client(
356
- status_codes=tuple(range(500, 600)),
357
- retry_condition=github_retry_policy(auth),
358
- ).session,
355
+ session=github_http_session(github_retry_policy(auth)),
359
356
  )
360
357
 
361
358
  def clients(auth: AuthConfigBase) -> tuple[RESTClient, RESTClient]:
@@ -0,0 +1,153 @@
1
+ """Exercise exhausted retries through the real parallel DLT resource wrappers."""
2
+
3
+ import json
4
+ import logging
5
+ from collections import Counter
6
+ from threading import Lock
7
+ from types import SimpleNamespace
8
+ from urllib.parse import parse_qs, urlparse
9
+
10
+ import dlt
11
+ import pytest
12
+ from dlt.sources.helpers.rest_client.auth import BearerTokenAuth
13
+ from dlt.sources.helpers.rest_client.client import RESTClient
14
+ from dlt.sources.helpers.rest_client.paginators import HeaderLinkPaginator
15
+ from requests import Response
16
+ from requests.exceptions import ReadTimeout
17
+
18
+ from openhound_github.github_rest_client import github_http_session
19
+ from openhound_github.helpers import github_retry_policy
20
+ from openhound_github.resources.organization import (
21
+ OrgContext,
22
+ SourceContext,
23
+ environment_branch_policies,
24
+ environment_secrets,
25
+ environment_variables,
26
+ environments,
27
+ organizations,
28
+ )
29
+
30
+
31
+ def _client(monkeypatch, route):
32
+ session = github_http_session(github_retry_policy(BearerTokenAuth("test")))
33
+ session.send.retry.sleep = lambda _: None
34
+ calls = Counter()
35
+ lock = Lock()
36
+
37
+ def send(request, **kwargs):
38
+ url = urlparse(request.url)
39
+ page = parse_qs(url.query).get("page", ["1"])[0]
40
+ with lock:
41
+ calls[url.path, page] += 1
42
+ outcome = route(url.path, page)
43
+ if isinstance(outcome, Exception):
44
+ raise outcome
45
+ status, payload, more = outcome
46
+ response = Response()
47
+ response.status_code = status
48
+ response.url = request.url
49
+ response.request = request
50
+ response._content = json.dumps(payload).encode()
51
+ if more:
52
+ response.headers["Link"] = f'<https://api.github.com{url.path}?page=2>; rel="next"'
53
+ return response
54
+
55
+ monkeypatch.setattr(session.get_adapter("https://"), "send", send)
56
+ return RESTClient(
57
+ "https://api.github.com", session=session, paginator=HeaderLinkPaginator(),
58
+ ), calls
59
+
60
+
61
+ def _failure(error):
62
+ return ReadTimeout("simulated read timeout") if error == "timeout" else (
63
+ 503, {"message": "simulated unavailable endpoint"}, False,
64
+ )
65
+
66
+
67
+ def _field(row, name):
68
+ return row[name] if isinstance(row, dict) else getattr(row, name)
69
+
70
+
71
+ @pytest.mark.parametrize("error", ["503", "timeout"])
72
+ def test_later_organizations_collect_after_exhausted_retries(monkeypatch, caplog, error):
73
+ def route(path, page):
74
+ if path == "/orgs/bad":
75
+ return _failure(error)
76
+ org = path.split("/")[2]
77
+ return 200, {"login": org, "node_id": f"ORG_{org}"}, False
78
+
79
+ client, calls = _client(monkeypatch, route)
80
+ ctx = SourceContext(client=client, organizations=[
81
+ OrgContext(client=client, org_name=name) for name in ("first", "bad", "last")
82
+ ])
83
+
84
+ with caplog.at_level(logging.ERROR):
85
+ rows = list(organizations(ctx))
86
+
87
+ assert {_field(row, "login") for row in rows} == {"first", "last"}
88
+ assert calls["/orgs/bad", "1"] == 3
89
+ assert "processing organization 'bad'" in caplog.text
90
+
91
+
92
+ @pytest.mark.parametrize("error", ["503", "timeout"])
93
+ def test_later_repositories_collect_and_partial_page_is_retained(monkeypatch, caplog, error):
94
+ def route(path, page):
95
+ repo = path.split("/")[3]
96
+ if repo == "bad" and page == "2":
97
+ return _failure(error)
98
+ environment = {
99
+ "id": 1, "node_id": f"ENV_{repo}", "name": repo,
100
+ "url": "https://api.github.com/example", "html_url": "https://github.com/example",
101
+ "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z",
102
+ }
103
+ return 200, {"environments": [environment]}, repo == "bad"
104
+
105
+ client, calls = _client(monkeypatch, route)
106
+ ctx = SourceContext(client=client, organizations=[OrgContext(client=client, org_name="org")])
107
+ parents = dlt.resource([
108
+ SimpleNamespace(id=f"REPO_{name}", name=name, org_login="org", environment_count=1)
109
+ for name in ("first", "bad", "last")
110
+ ], name="probe_repositories")
111
+
112
+ with caplog.at_level(logging.ERROR):
113
+ rows = list(parents | environments(ctx))
114
+
115
+ assert {_field(row, "repository_name") for row in rows} == {"first", "bad", "last"}
116
+ assert calls["/repos/org/bad/environments", "2"] == 3
117
+ assert "resource 'environments'" in caplog.text
118
+ assert "/repos/org/bad/environments" in caplog.text or error == "timeout"
119
+
120
+
121
+ @pytest.mark.parametrize("transformer, suffix, selector", [
122
+ (environment_variables, "variables", "variables"),
123
+ (environment_secrets, "secrets", "secrets"),
124
+ (environment_branch_policies, "deployment-branch-policies", "branch_policies"),
125
+ ])
126
+ @pytest.mark.parametrize("error", ["503", "timeout"])
127
+ def test_later_environments_collect_after_partial_failure(
128
+ monkeypatch, caplog, transformer, suffix, selector, error,
129
+ ):
130
+ def route(path, page):
131
+ name = path.split("/")[5]
132
+ if name == "bad" and page == "2":
133
+ return _failure(error)
134
+ item = {"name": "item", "value": "value", "id": 1, "node_id": "POLICY",
135
+ "created_at": "2026-01-01T00:00:00Z"}
136
+ return 200, {selector: [item]}, name == "bad"
137
+
138
+ client, calls = _client(monkeypatch, route)
139
+ ctx = SourceContext(client=client, organizations=[OrgContext(client=client, org_name="org")])
140
+ parents = dlt.resource([
141
+ SimpleNamespace(
142
+ name=name, node_id=f"ENV_{name}", org_login="org", repository_name="repo",
143
+ repository_full_name="org/repo", repository_node_id="REPO",
144
+ has_custom_branch_policies=True, required_reviewers=False, prevent_self_review=False,
145
+ ) for name in ("first", "bad", "last")
146
+ ], name="probe_environments")
147
+
148
+ with caplog.at_level(logging.ERROR):
149
+ rows = list(parents | transformer(ctx))
150
+
151
+ assert {_field(row, "environment_name") for row in rows} == {"first", "bad", "last"}
152
+ assert calls[f"/repos/org/repo/environments/bad/{suffix}", "2"] == 3
153
+ assert f"resource '{transformer.name}'" in caplog.text
@@ -4,10 +4,12 @@ from datetime import datetime, timedelta, timezone
4
4
  from threading import Event, Lock
5
5
 
6
6
  import requests
7
+ import pytest
7
8
  from dlt.sources.helpers.rest_client.auth import BearerTokenAuth
8
9
 
9
10
  from openhound_github.auth import GitHubAppInstallationAuth, TokenResponse
10
11
  from openhound_github.helpers import github_retry_policy
12
+ from openhound_github.github_rest_client import github_http_session
11
13
 
12
14
 
13
15
  class FakeInstallation:
@@ -243,3 +245,37 @@ def test_retry_policy_does_not_repair_bad_credentials_for_bearer_token_auth() ->
243
245
 
244
246
  assert should_retry is False
245
247
  assert request.headers["Authorization"] == "Bearer static-token"
248
+
249
+
250
+ @pytest.mark.parametrize("replacement_rejected", [False, True])
251
+ def test_http_session_retries_with_refreshed_credentials(monkeypatch, replacement_rejected):
252
+ installation = FakeInstallation("new-token")
253
+ auth = GitHubAppInstallationAuth(installation=installation)
254
+ auth.access_token = "old-token"
255
+ auth.expires_at = datetime.now(timezone.utc) + timedelta(hours=1)
256
+ session = github_http_session(github_retry_policy(auth))
257
+ session.auth = auth
258
+ session.send.retry.sleep = lambda _: None
259
+ # Two transient failures must leave room for credential recovery.
260
+ statuses = iter([503, 503, 401, 401 if replacement_rejected else 200])
261
+ authorizations = []
262
+
263
+ def send(request, **kwargs):
264
+ authorizations.append(request.headers.get("Authorization"))
265
+ response = requests.Response()
266
+ response.status_code = next(statuses)
267
+ response.request = request
268
+ response.url = request.url
269
+ response._content = b'{"message":"Bad credentials"}'
270
+ return response
271
+
272
+ monkeypatch.setattr(session.get_adapter("https://"), "send", send)
273
+ if replacement_rejected:
274
+ with pytest.raises(requests.HTTPError) as caught:
275
+ session.get("https://api.github.com/example")
276
+ assert caught.value.response.status_code == 401
277
+ else:
278
+ assert session.get("https://api.github.com/example").status_code == 200
279
+
280
+ assert authorizations == ["Bearer old-token"] * 3 + ["Bearer new-token"]
281
+ assert installation.token_calls == 1
@@ -0,0 +1,161 @@
1
+ import json
2
+ from unittest.mock import Mock
3
+
4
+ import pytest
5
+ from dlt.sources.helpers import requests as dlt_requests
6
+ from requests import HTTPError, Response
7
+ from requests.exceptions import ChunkedEncodingError, ConnectionError, ReadTimeout
8
+ from dlt.sources.helpers.rest_client.auth import BearerTokenAuth
9
+
10
+ from openhound_github.github_rest_client import github_http_session, should_retry_github_response
11
+ from openhound_github.helpers import github_retry_policy
12
+
13
+
14
+ def make_response(
15
+ status_code: int,
16
+ *,
17
+ headers: dict[str, str] | None = None,
18
+ body: dict[str, object] | None = None,
19
+ text: str | None = None,
20
+ ) -> Response:
21
+ response = Response()
22
+ response.status_code = status_code
23
+ response.headers.update(headers or {})
24
+ if body is not None:
25
+ response._content = json.dumps(body).encode("utf-8")
26
+ response.headers.setdefault("Content-Type", "application/json")
27
+ else:
28
+ response._content = (text or "").encode("utf-8")
29
+ response.url = "https://api.github.com/example"
30
+ return response
31
+
32
+
33
+ def test_retries_primary_rate_limit_response() -> None:
34
+ response = make_response(
35
+ 403,
36
+ headers={"x-ratelimit-remaining": "0"},
37
+ body={"message": "API rate limit exceeded"},
38
+ )
39
+
40
+ assert should_retry_github_response(response, None) is True
41
+
42
+
43
+ def test_retries_secondary_rate_limit_response() -> None:
44
+ response = make_response(
45
+ 429,
46
+ body={"message": "You have exceeded a secondary rate limit."},
47
+ )
48
+
49
+ assert should_retry_github_response(response, None) is True
50
+
51
+
52
+ def test_does_not_retry_non_rate_limit_403() -> None:
53
+ response = make_response(
54
+ 403,
55
+ body={"message": "Resource not accessible by integration"},
56
+ )
57
+
58
+ assert should_retry_github_response(response, None) is False
59
+
60
+
61
+ def _session(monkeypatch, outcomes):
62
+ session = github_http_session(github_retry_policy(BearerTokenAuth("test")))
63
+ send = Mock(side_effect=outcomes)
64
+ monkeypatch.setattr(session.get_adapter("https://"), "send", send)
65
+ session.send.retry.sleep = lambda _: None
66
+ return session, send
67
+
68
+
69
+ @pytest.mark.parametrize("status", [500, 502, 503, 504])
70
+ def test_server_failures_stop_after_three_attempts(monkeypatch, status):
71
+ session, send = _session(monkeypatch, [make_response(status) for _ in range(4)])
72
+
73
+ with pytest.raises(HTTPError) as caught:
74
+ session.get("https://api.github.com/example")
75
+
76
+ assert caught.value.response.status_code == status
77
+ assert send.call_count == 3
78
+ assert all(call.kwargs["timeout"] == (10, 30) for call in send.call_args_list)
79
+
80
+
81
+ @pytest.mark.parametrize("error", [ReadTimeout, ConnectionError, ChunkedEncodingError])
82
+ def test_network_failures_stop_after_three_attempts(monkeypatch, error):
83
+ session, send = _session(monkeypatch, [error("interrupted") for _ in range(4)])
84
+
85
+ with pytest.raises(error):
86
+ session.get("https://api.github.com/example")
87
+
88
+ assert send.call_count == 3
89
+
90
+
91
+ def test_transient_failure_count_resets_for_each_request(monkeypatch):
92
+ session, send = _session(monkeypatch, [
93
+ make_response(503), make_response(503), make_response(200),
94
+ make_response(503), make_response(503), make_response(200),
95
+ ])
96
+
97
+ assert session.get("https://api.github.com/example").status_code == 200
98
+ assert session.get("https://api.github.com/example").status_code == 200
99
+ assert send.call_count == 6
100
+
101
+
102
+ def test_mixed_server_and_network_failures_share_budget(monkeypatch):
103
+ session, send = _session(monkeypatch, [
104
+ make_response(503), ReadTimeout("interrupted"), make_response(502),
105
+ make_response(200),
106
+ ])
107
+
108
+ with pytest.raises(HTTPError):
109
+ session.get("https://api.github.com/example")
110
+
111
+ assert send.call_count == 3
112
+
113
+
114
+ def test_rate_limit_retries_do_not_consume_transient_budget(monkeypatch):
115
+ session, send = _session(monkeypatch, [
116
+ *[make_response(429, body={"message": "secondary rate limit"}) for _ in range(4)],
117
+ make_response(503), make_response(503), make_response(200),
118
+ ])
119
+
120
+ assert session.get("https://api.github.com/example").status_code == 200
121
+ assert send.call_count == 7
122
+
123
+
124
+ @pytest.mark.parametrize(("status", "headers", "body"), [
125
+ (403, {"x-ratelimit-remaining": "0", "x-ratelimit-reset": "4102444800"},
126
+ {"message": "API rate limit exceeded"}),
127
+ (429, {}, {"message": "secondary rate limit"}),
128
+ (429, {"Retry-After": "12"}, {"message": "Please wait"}),
129
+ (200, {"x-ratelimit-resource": "graphql", "x-ratelimit-remaining": "0",
130
+ "x-ratelimit-reset": "4102444800"},
131
+ {"errors": [{"message": "API rate limit exceeded"}]}),
132
+ ])
133
+ def test_rate_limit_attempts_and_waits_match_existing_session(
134
+ monkeypatch, status, headers, body,
135
+ ):
136
+ monkeypatch.setenv("RUNTIME__REQUEST_MAX_ATTEMPTS", "5")
137
+ observations = []
138
+ for updated in (False, True):
139
+ policy = github_retry_policy(BearerTokenAuth("test"))
140
+ session = (
141
+ github_http_session(policy) if updated else dlt_requests.Client(
142
+ status_codes=tuple(range(500, 600)), retry_condition=policy,
143
+ ).session
144
+ )
145
+ send = Mock(side_effect=[
146
+ make_response(status, headers=headers, body=body) for _ in range(5)
147
+ ])
148
+ monkeypatch.setattr(session.get_adapter("https://"), "send", send)
149
+ sleep = Mock()
150
+ session.send.retry.sleep = sleep
151
+ if status != 200:
152
+ with pytest.raises(HTTPError):
153
+ session.get("https://api.github.com/example")
154
+ else:
155
+ response = session.get("https://api.github.com/graphql")
156
+ assert response.json()["errors"]
157
+ observations.append((send.call_count, [float(c.args[0]) for c in sleep.call_args_list]))
158
+
159
+ assert observations[0][0] == 5
160
+ assert len(observations[0][1]) == 4
161
+ assert observations[1] == observations[0]