openhound-github 0.18.0__tar.gz → 0.19.0__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 (443) hide show
  1. {openhound_github-0.18.0 → openhound_github-0.19.0}/PKG-INFO +17 -1
  2. {openhound_github-0.18.0 → openhound_github-0.19.0}/README.md +16 -0
  3. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_AppInstallation.md +1 -1
  4. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/lookup.py +10 -0
  5. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/app_installation.py +10 -2
  6. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/resources/enterprise.py +29 -3
  7. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/resources/organization.py +84 -0
  8. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/source.py +1 -0
  9. openhound_github-0.19.0/tests/test_app_installation_repo_access.py +130 -0
  10. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_classic_personal_access_tokens.py +73 -9
  11. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/CODE_OF_CONDUCT.md +0 -0
  12. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/CONTRIBUTING.md +0 -0
  13. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GOVERNANCE.md +0 -0
  14. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GitHub-COC-Header.png +0 -0
  15. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GitHub-CONTRIB-Header.png +0 -0
  16. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GitHub-GOV-Header.png +0 -0
  17. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GitHub-Header.png +0 -0
  18. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GitHub-LIC-Header.png +0 -0
  19. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/GitHub-SEC-Header.png +0 -0
  20. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/SECURITY.md +0 -0
  21. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/SO-Dark.svg +0 -0
  22. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/SO-FOSS-Dark.svg +0 -0
  23. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/SO-FOSS-Light.svg +0 -0
  24. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/SO-Light.svg +0 -0
  25. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/workflows/publish.yml +0 -0
  26. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/workflows/release-on-merge.yml +0 -0
  27. {openhound_github-0.18.0 → openhound_github-0.19.0}/.github/workflows/validate-branch.yml +0 -0
  28. {openhound_github-0.18.0 → openhound_github-0.19.0}/.gitignore +0 -0
  29. {openhound_github-0.18.0 → openhound_github-0.19.0}/.gitmodules +0 -0
  30. {openhound_github-0.18.0 → openhound_github-0.19.0}/.pre-commit-config.yaml +0 -0
  31. {openhound_github-0.18.0 → openhound_github-0.19.0}/LICENSE.md +0 -0
  32. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AddAssignee.md +0 -0
  33. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AddCollaborator.md +0 -0
  34. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AddLabel.md +0 -0
  35. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AddMember.md +0 -0
  36. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AddedDeployKey.md +0 -0
  37. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AdminTo.md +0 -0
  38. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ApprovesDeploymentTo.md +0 -0
  39. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AssignedTo.md +0 -0
  40. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_AuthorizedForOrganization.md +0 -0
  41. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_BypassBranchProtection.md +0 -0
  42. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_BypassPullRequestAllowances.md +0 -0
  43. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CallsWorkflow.md +0 -0
  44. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanAccess.md +0 -0
  45. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanAccessSecret.md +0 -0
  46. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanAssumeIdentity.md +0 -0
  47. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreateBranch.md +0 -0
  48. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreateEnvironment.md +0 -0
  49. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreateInternalRepositories.md +0 -0
  50. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreatePrivateRepositories.md +0 -0
  51. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreatePublicRepositories.md +0 -0
  52. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreateRepositories.md +0 -0
  53. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanCreateRepositoryWithRunnerAccess.md +0 -0
  54. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanDeployToEnvironment.md +0 -0
  55. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanEditEnvironment.md +0 -0
  56. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanEditProtection.md +0 -0
  57. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanInterceptJob.md +0 -0
  58. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanPwnRequest.md +0 -0
  59. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanReadSecret.md +0 -0
  60. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanReadSecretScanningAlert.md +0 -0
  61. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanRequestOIDCTokenFor.md +0 -0
  62. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanUseRunner.md +0 -0
  63. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CanWriteBranch.md +0 -0
  64. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CloseDiscussion.md +0 -0
  65. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CloseIssue.md +0 -0
  66. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ClosePullRequest.md +0 -0
  67. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_Contains.md +0 -0
  68. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ConvertIssuesToDiscussions.md +0 -0
  69. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CreateDiscussionCategory.md +0 -0
  70. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CreateEnterpriseOrganizations.md +0 -0
  71. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CreateRepository.md +0 -0
  72. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CreateSoloMergeQueueEntry.md +0 -0
  73. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CreateTag.md +0 -0
  74. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_CreateTeam.md +0 -0
  75. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DeleteAlertsCodeScanning.md +0 -0
  76. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DeleteDiscussion.md +0 -0
  77. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DeleteDiscussionComment.md +0 -0
  78. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DeleteIssue.md +0 -0
  79. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DeleteTag.md +0 -0
  80. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DependsOn.md +0 -0
  81. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_DeploysTo.md +0 -0
  82. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditCategoryOnDiscussion.md +0 -0
  83. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditDiscussionCategory.md +0 -0
  84. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditDiscussionComment.md +0 -0
  85. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditEnterpriseCustomPropertiesForOrganizations.md +0 -0
  86. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditRepoAnnouncementBanners.md +0 -0
  87. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditRepoCustomPropertiesValues.md +0 -0
  88. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditRepoMetadata.md +0 -0
  89. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_EditRepoProtections.md +0 -0
  90. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasBaseRole.md +0 -0
  91. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasExternalIdentity.md +0 -0
  92. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasMember.md +0 -0
  93. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasPersonalAccessToken.md +0 -0
  94. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasPersonalAccessTokenRequest.md +0 -0
  95. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasRole.md +0 -0
  96. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasRunner.md +0 -0
  97. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasSamlIdentityProvider.md +0 -0
  98. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasSecret.md +0 -0
  99. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_HasVariable.md +0 -0
  100. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_InheritedFrom.md +0 -0
  101. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_InstalledAs.md +0 -0
  102. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_InviteMember.md +0 -0
  103. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_IsEligibleFor.md +0 -0
  104. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_JumpMergeQueue.md +0 -0
  105. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageDeployKeys.md +0 -0
  106. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageDiscussionBadges.md +0 -0
  107. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseAdmins.md +0 -0
  108. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseIdentityProvider.md +0 -0
  109. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseMembers.md +0 -0
  110. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseOrganizationAdmins.md +0 -0
  111. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseOrganizations.md +0 -0
  112. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseReferrals.md +0 -0
  113. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageEnterpriseTeams.md +0 -0
  114. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageOrganizationWebhooks.md +0 -0
  115. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageRepoSecurityProducts.md +0 -0
  116. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageSecurityProducts.md +0 -0
  117. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageSettingsMergeTypes.md +0 -0
  118. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageSettingsPages.md +0 -0
  119. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageSettingsProjects.md +0 -0
  120. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageSettingsWiki.md +0 -0
  121. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageTopics.md +0 -0
  122. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ManageWebhooks.md +0 -0
  123. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_MapsToUser.md +0 -0
  124. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_MarkAsDuplicate.md +0 -0
  125. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_MatchesEnvironmentPolicy.md +0 -0
  126. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_MemberOf.md +0 -0
  127. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_OrgBypassCodeScanningDismissalRequests.md +0 -0
  128. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_OrgBypassSecretScanningClosureRequests.md +0 -0
  129. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_OrgReviewAndManageSecretScanningBypassRequests.md +0 -0
  130. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_OrgReviewAndManageSecretScanningClosureRequests.md +0 -0
  131. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_Owns.md +0 -0
  132. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ProtectedBy.md +0 -0
  133. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_PushProtectedBranch.md +0 -0
  134. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadCodeScanning.md +0 -0
  135. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadEnterpriseAuditLog.md +0 -0
  136. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadEnterpriseDomainVerification.md +0 -0
  137. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadEnterpriseMembers.md +0 -0
  138. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadEnterpriseOrgProjects.md +0 -0
  139. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadEnterpriseOrganizationAdmin.md +0 -0
  140. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadOrganizationActionsUsageMetrics.md +0 -0
  141. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadOrganizationCustomOrgRole.md +0 -0
  142. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadOrganizationCustomRepoRole.md +0 -0
  143. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReadRepoContents.md +0 -0
  144. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_RemoveAssignee.md +0 -0
  145. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_RemoveLabel.md +0 -0
  146. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReopenDiscussion.md +0 -0
  147. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReopenIssue.md +0 -0
  148. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ReopenPullRequest.md +0 -0
  149. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_RequestPrReview.md +0 -0
  150. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ResolveDependabotAlerts.md +0 -0
  151. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ResolveSecretScanningAlerts.md +0 -0
  152. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_RestrictionsCanPush.md +0 -0
  153. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_RunOrgMigration.md +0 -0
  154. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_RunsOn.md +0 -0
  155. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_SetEnterpriseInteractionLimits.md +0 -0
  156. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_SetInteractionLimits.md +0 -0
  157. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_SetIssueType.md +0 -0
  158. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_SetMilestone.md +0 -0
  159. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_SetSocialPreview.md +0 -0
  160. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_SyncedTo.md +0 -0
  161. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ToggleDiscussionAnswer.md +0 -0
  162. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ToggleDiscussionCommentMinimize.md +0 -0
  163. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_TransferRepository.md +0 -0
  164. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_UsesSecret.md +0 -0
  165. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_UsesVariable.md +0 -0
  166. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ValidToken.md +0 -0
  167. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ViewDependabotAlerts.md +0 -0
  168. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ViewEnterpriseActionsUsageMetrics.md +0 -0
  169. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ViewEnterpriseBilling.md +0 -0
  170. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ViewEnterpriseSecretScanningAlerts.md +0 -0
  171. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_ViewSecretScanningAlerts.md +0 -0
  172. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteCodeScanning.md +0 -0
  173. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteEnterpriseActionsPolicies.md +0 -0
  174. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteEnterpriseBilling.md +0 -0
  175. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteEnterprisePersonalAccessTokenPolicies.md +0 -0
  176. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteEnterpriseSso.md +0 -0
  177. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteEnterpriseTeamMembers.md +0 -0
  178. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteOrganizationActionsSecrets.md +0 -0
  179. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteOrganizationActionsSettings.md +0 -0
  180. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteOrganizationActionsVariables.md +0 -0
  181. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteOrganizationCustomOrgRole.md +0 -0
  182. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteOrganizationCustomRepoRole.md +0 -0
  183. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteOrganizationNetworkConfigurations.md +0 -0
  184. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteRepoContents.md +0 -0
  185. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/edges/GH_WriteRepoPullRequests.md +0 -0
  186. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_App.md +0 -0
  187. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Branch.md +0 -0
  188. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_BranchProtectionRule.md +0 -0
  189. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_ClassicPersonalAccessToken.md +0 -0
  190. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_DeployKey.md +0 -0
  191. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Enterprise.md +0 -0
  192. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnterpriseManagedUser.md +0 -0
  193. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnterpriseRole.md +0 -0
  194. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnterpriseRunner.md +0 -0
  195. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnterpriseRunnerGroup.md +0 -0
  196. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnterpriseTeam.md +0 -0
  197. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Environment.md +0 -0
  198. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnvironmentBranchPolicy.md +0 -0
  199. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnvironmentSecret.md +0 -0
  200. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_EnvironmentVariable.md +0 -0
  201. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_ExternalIdentity.md +0 -0
  202. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_OrgRole.md +0 -0
  203. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_OrgRunner.md +0 -0
  204. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_OrgRunnerGroup.md +0 -0
  205. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_OrgSecret.md +0 -0
  206. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_OrgVariable.md +0 -0
  207. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Organization.md +0 -0
  208. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_PersonalAccessToken.md +0 -0
  209. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_PersonalAccessTokenRequest.md +0 -0
  210. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_RepoRole.md +0 -0
  211. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_RepoRunner.md +0 -0
  212. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_RepoSecret.md +0 -0
  213. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_RepoVariable.md +0 -0
  214. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Repository.md +0 -0
  215. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Runner.md +0 -0
  216. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_RunnerGroup.md +0 -0
  217. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_SamlIdentityProvider.md +0 -0
  218. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Secret.md +0 -0
  219. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_SecretScanningAlert.md +0 -0
  220. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Team.md +0 -0
  221. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_TeamRole.md +0 -0
  222. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_User.md +0 -0
  223. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Variable.md +0 -0
  224. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_Workflow.md +0 -0
  225. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_WorkflowJob.md +0 -0
  226. {openhound_github-0.18.0 → openhound_github-0.19.0}/descriptions/nodes/GH_WorkflowStep.md +0 -0
  227. {openhound_github-0.18.0 → openhound_github-0.19.0}/docs/og-docs.json +0 -0
  228. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-all-repo-admin-role.json +0 -0
  229. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-app-installations-all-repos.json +0 -0
  230. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-apps-all-repos.json +0 -0
  231. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-enterprise-owners-role.json +0 -0
  232. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-external-identities-owners.json +0 -0
  233. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-organizations.json +0 -0
  234. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-owner-users.json +0 -0
  235. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-owners-role.json +0 -0
  236. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-pats-all-repos.json +0 -0
  237. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-privilege-escalation-roles.json +0 -0
  238. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-privilege-escalation-users.json +0 -0
  239. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/privilege_zone_rules/t0-saml-identity-providers.json +0 -0
  240. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/README.md +0 -0
  241. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/actions-sha-pinning-not-required.json +0 -0
  242. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/active-leaked-secrets.json +0 -0
  243. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/advanced-security-disabled-new-repos.json +0 -0
  244. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/all-actions-allowed.json +0 -0
  245. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/app-installations-all-repos.json +0 -0
  246. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-admins-not-enforced.json +0 -0
  247. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-deletions-allowed.json +0 -0
  248. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-force-pushes.json +0 -0
  249. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-no-code-owner-reviews.json +0 -0
  250. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-no-pr-reviews.json +0 -0
  251. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-no-status-checks.json +0 -0
  252. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-self-approval.json +0 -0
  253. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/branch-protection-stale-reviews.json +0 -0
  254. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/bypass-pr-requirements.json +0 -0
  255. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/dangerous-branch-perms.json +0 -0
  256. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/default-repository-permissions.json +0 -0
  257. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/demo-sso-to-cloud-round-trip.json +0 -0
  258. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/dependabot-alerts-disabled-new-repos.json +0 -0
  259. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/dependabot-updates-disabled-new-repos.json +0 -0
  260. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/dependency-graph-disabled-new-repos.json +0 -0
  261. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/environments-admin-bypass.json +0 -0
  262. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/expired-pats.json +0 -0
  263. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/external-identities-without-scim.json +0 -0
  264. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/github-to-azure-identity.json +0 -0
  265. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/global-repo-perms.json +0 -0
  266. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/hybrid-identities.json +0 -0
  267. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/members-can-change-repo-visibility.json +0 -0
  268. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/members-can-create-pages.json +0 -0
  269. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/members-can-create-public-repos.json +0 -0
  270. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/members-can-delete-repos.json +0 -0
  271. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/members-can-fork-private-repos.json +0 -0
  272. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/members-can-invite-outside-collaborators.json +0 -0
  273. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/org-owners.json +0 -0
  274. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/orgs-without-2fa.json +0 -0
  275. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/pats-all-repo-access.json +0 -0
  276. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/pending-pat-requests.json +0 -0
  277. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/private-repos-forking-allowed.json +0 -0
  278. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/privileged-custom-org-roles.json +0 -0
  279. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/privileged-hybrid-identities.json +0 -0
  280. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/public-repos.json +0 -0
  281. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/push-protection-disabled-new-repos.json +0 -0
  282. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/push-to-protected-branches.json +0 -0
  283. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/repos-secret-scanning-disabled.json +0 -0
  284. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/repos-vulnerable-to-workflow-secret-exfil.json +0 -0
  285. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/repository-workflows.json +0 -0
  286. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/saml-configuration.json +0 -0
  287. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/secret-scanning-alerts.json +0 -0
  288. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/secret-scanning-disabled-new-repos.json +0 -0
  289. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/secrets-reachable-by-user.json +0 -0
  290. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/self-hosted-runners-can-intercept-broad-token-jobs.json +0 -0
  291. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/shared-self-hosted-runners-can-intercept-secret-bearing-jobs.json +0 -0
  292. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/team-membership-admin.json +0 -0
  293. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/team-structure.json +0 -0
  294. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/unprotected-branches.json +0 -0
  295. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/unprotected-default-branch-with-workflow.json +0 -0
  296. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/unprotected-default-branches.json +0 -0
  297. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/web-commit-signoff-not-required.json +0 -0
  298. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/workflow-jobs-interceptable-by-self-hosted-runners.json +0 -0
  299. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/workflow-jobs-with-broad-token-write-permissions.json +0 -0
  300. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/workflow-jobs-with-id-token-write-on-self-hosted-runners.json +0 -0
  301. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/workflow-jobs-with-id-token-write.json +0 -0
  302. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/saved_searches/workflow-jobs-with-observed-oidc-auth-steps.json +0 -0
  303. {openhound_github-0.18.0 → openhound_github-0.19.0}/extension/schema.json +0 -0
  304. {openhound_github-0.18.0 → openhound_github-0.19.0}/justfile +0 -0
  305. {openhound_github-0.18.0 → openhound_github-0.19.0}/pyproject.toml +0 -0
  306. {openhound_github-0.18.0 → openhound_github-0.19.0}/scripts/generate_gh_description_docs.py +0 -0
  307. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/main.py +0 -0
  308. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/__init__.py +0 -0
  309. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/auth.py +0 -0
  310. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/extension.yaml +0 -0
  311. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/github_rest_client.py +0 -0
  312. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/github_retry.py +0 -0
  313. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/graph.py +0 -0
  314. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/graphql.py +0 -0
  315. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/helpers.py +0 -0
  316. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/kinds/edges.py +0 -0
  317. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/kinds/nodes.py +0 -0
  318. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/main.py +0 -0
  319. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/__init__.py +0 -0
  320. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/actions_permission.py +0 -0
  321. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/branch.py +0 -0
  322. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/branch_pr_bypass_allowance.py +0 -0
  323. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/branch_protection_rule.py +0 -0
  324. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/branch_push_allowance.py +0 -0
  325. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/classic_personal_access_token.py +0 -0
  326. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/deploy_key.py +0 -0
  327. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise.py +0 -0
  328. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_admin.py +0 -0
  329. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_helpers.py +0 -0
  330. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_managed_user.py +0 -0
  331. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_member.py +0 -0
  332. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_organization.py +0 -0
  333. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_role.py +0 -0
  334. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_role_team.py +0 -0
  335. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_role_user.py +0 -0
  336. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_team.py +0 -0
  337. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_team_member.py +0 -0
  338. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_team_organization.py +0 -0
  339. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_team_role.py +0 -0
  340. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/enterprise_user.py +0 -0
  341. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/env_secret.py +0 -0
  342. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/env_variable.py +0 -0
  343. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/environment.py +0 -0
  344. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/environment_branch_policy.py +0 -0
  345. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/external_identity.py +0 -0
  346. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/org.py +0 -0
  347. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/org_role.py +0 -0
  348. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/org_role_member.py +0 -0
  349. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/org_role_team.py +0 -0
  350. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/org_secret.py +0 -0
  351. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/org_variable.py +0 -0
  352. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/permissions.py +0 -0
  353. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/personal_access_token.py +0 -0
  354. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/personal_access_token_access.py +0 -0
  355. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/personal_access_token_request.py +0 -0
  356. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/projected_enterprise_team.py +0 -0
  357. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/repo_role_assignment.py +0 -0
  358. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/repository.py +0 -0
  359. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/repository_role.py +0 -0
  360. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/repository_secret.py +0 -0
  361. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/repository_variable.py +0 -0
  362. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/runner.py +0 -0
  363. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/saml_assertion_consumer_service.py +0 -0
  364. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/saml_helpers.py +0 -0
  365. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/saml_issuer.py +0 -0
  366. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/saml_provider.py +0 -0
  367. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/saml_service_provider.py +0 -0
  368. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/scim_user.py +0 -0
  369. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/secret_scanning_alert.py +0 -0
  370. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/team.py +0 -0
  371. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/team_member.py +0 -0
  372. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/team_role.py +0 -0
  373. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/user.py +0 -0
  374. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/workflow.py +0 -0
  375. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/workflow_job.py +0 -0
  376. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/workflow_reference.py +0 -0
  377. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/models/workflow_step.py +0 -0
  378. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/resources/__init__.py +0 -0
  379. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/runner_ids.py +0 -0
  380. {openhound_github-0.18.0 → openhound_github-0.19.0}/src/openhound_github/transforms.py +0 -0
  381. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_app_auth.py +0 -0
  382. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_branch_collection_gate.py +0 -0
  383. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_branch_protection_traversability.py +0 -0
  384. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_credential_permission_models.py +0 -0
  385. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/actions-permissions.json +0 -0
  386. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/actionsecrets.json +0 -0
  387. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/app.json +0 -0
  388. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/appinstalls.json +0 -0
  389. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/branch-policies.json +0 -0
  390. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/branch-prot-rules.json +0 -0
  391. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/branches.json +0 -0
  392. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/custom-roles.json +0 -0
  393. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/environments.json +0 -0
  394. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/org-pat-request.json +0 -0
  395. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/org-pat.json +0 -0
  396. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/org-role-teams.json +0 -0
  397. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/org-role-users.json +0 -0
  398. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/org-roles.json +0 -0
  399. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/org.json +0 -0
  400. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/pat-repositories.json +0 -0
  401. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/repo-collaborators.json +0 -0
  402. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/repo-teams.json +0 -0
  403. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/repositories.json +0 -0
  404. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/scimusers.json +0 -0
  405. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/secrets.json +0 -0
  406. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/secretscanningalert.json +0 -0
  407. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/selected-org-secrets.json +0 -0
  408. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/variables.json +0 -0
  409. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_data/api/workflows.json +0 -0
  410. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_deploy_key_models.py +0 -0
  411. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_deploy_key_resources.py +0 -0
  412. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_description_docs.py +0 -0
  413. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_enterprise_capabilities.py +0 -0
  414. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_enterprise_organization.py +0 -0
  415. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_enterprise_resources.py +0 -0
  416. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_environment_model.py +0 -0
  417. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_environment_resources.py +0 -0
  418. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_environment_reviewer_lookup.py +0 -0
  419. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_error_resilience.py +0 -0
  420. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_github_app_retry.py +0 -0
  421. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_github_rest_client.py +0 -0
  422. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_github_retry.py +0 -0
  423. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_helpers.py +0 -0
  424. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_lookup.py +0 -0
  425. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_org_role_model.py +0 -0
  426. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_org_scim_resources.py +0 -0
  427. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_org_secret_models.py +0 -0
  428. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_preproc.py +0 -0
  429. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_repository_rulesets.py +0 -0
  430. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_runner_ids.py +0 -0
  431. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_runner_models.py +0 -0
  432. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_runner_resources.py +0 -0
  433. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_saml_helpers.py +0 -0
  434. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_saml_models.py +0 -0
  435. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_scim_models.py +0 -0
  436. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_source_endpoints.py +0 -0
  437. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_source_org_canonicalization.py +0 -0
  438. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_team_external_groups.py +0 -0
  439. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_variable_models.py +0 -0
  440. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_workflow_interception_path.py +0 -0
  441. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_workflow_model.py +0 -0
  442. {openhound_github-0.18.0 → openhound_github-0.19.0}/tests/test_workflow_resources.py +0 -0
  443. {openhound_github-0.18.0 → openhound_github-0.19.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openhound-github
3
- Version: 0.18.0
3
+ Version: 0.19.0
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
@@ -84,6 +84,22 @@ The collector runs successfully with the documented read-only permission set. So
84
84
 
85
85
  If these optional permissions are not granted, OpenHound skips the affected resources and continues collecting the rest of the GitHub environment.
86
86
 
87
+ ### Selected GitHub App installation repositories
88
+
89
+ For organization installations limited to selected repositories, the collector
90
+ uses the enterprise installation's read-only organization-installation
91
+ repositories API to create exact `GH_CanAccess` edges. Grant the enterprise
92
+ installation **Enterprise organization installations: read** or **Enterprise
93
+ organization installation repositories: read**. Without either permission,
94
+ selected-repository edges are skipped; installations set to `all` continue to
95
+ use the collected organization repository inventory.
96
+
97
+ Installation discovery still uses each organization's API and requires
98
+ **Organization administration: read** on that organization's credential. The
99
+ enterprise permission alone does not discover installations.
100
+ The selected-repository lookup runs one installation at a time and stops for
101
+ the rest of a collection if GitHub rate-limits it after retries.
102
+
87
103
  ### Classic personal access token inventory
88
104
 
89
105
  With GitHub Enterprise Cloud credentials, the collector uses the enterprise
@@ -73,6 +73,22 @@ The collector runs successfully with the documented read-only permission set. So
73
73
 
74
74
  If these optional permissions are not granted, OpenHound skips the affected resources and continues collecting the rest of the GitHub environment.
75
75
 
76
+ ### Selected GitHub App installation repositories
77
+
78
+ For organization installations limited to selected repositories, the collector
79
+ uses the enterprise installation's read-only organization-installation
80
+ repositories API to create exact `GH_CanAccess` edges. Grant the enterprise
81
+ installation **Enterprise organization installations: read** or **Enterprise
82
+ organization installation repositories: read**. Without either permission,
83
+ selected-repository edges are skipped; installations set to `all` continue to
84
+ use the collected organization repository inventory.
85
+
86
+ Installation discovery still uses each organization's API and requires
87
+ **Organization administration: read** on that organization's credential. The
88
+ enterprise permission alone does not discover installations.
89
+ The selected-repository lookup runs one installation at a time and stops for
90
+ the rest of a collection if GitHub rate-limits it after retries.
91
+
76
92
  ### Classic personal access token inventory
77
93
 
78
94
  With GitHub Enterprise Cloud credentials, the collector uses the enterprise
@@ -6,7 +6,7 @@ Represents a GitHub App installed on an organization. App installations have spe
6
6
 
7
7
  Unlike fine-grained personal access tokens, GitHub does not expose separate organization and repository permission buckets for app installations, so this property remains a single flat permission list.
8
8
 
9
- Each installation is linked to its parent GH_App via a GH_InstalledAs edge. For installations with `repository_selection` set to `all`, GH_CanAccess edges are created to every repository in the organization. For installations with `repository_selection` set to `selected`, repository-level edges cannot be enumerated with a PAT (requires app installation token authentication).
9
+ Each installation is linked to its parent GH_App via a GH_InstalledAs edge. For installations with `repository_selection` set to `all`, GH_CanAccess edges are created to every collected repository in the organization. For installations with `repository_selection` set to `selected`, the collector uses the enterprise organization-installation repositories API to enumerate the granted repositories when the enterprise installation has `Enterprise organization installations: read` or `Enterprise organization installation repositories: read`. If that permission is unavailable, selected repository edges are skipped.
10
10
 
11
11
  ## Properties
12
12
 
@@ -597,6 +597,16 @@ class GithubLookup(LookupManager):
597
597
  [org_login],
598
598
  )
599
599
 
600
+ @lru_cache
601
+ def repository_node_id_for_database_id(
602
+ self, database_id: int, org_login: str
603
+ ) -> str | None:
604
+ """Resolve a repository node by database ID within its organization."""
605
+ return self._find_single_object(
606
+ f"SELECT node_id FROM {self.schema}.repositories WHERE database_id = ? AND lower(org_login) = lower(?) LIMIT 1",
607
+ [database_id, org_login],
608
+ )
609
+
600
610
  @lru_cache
601
611
  def private_repository_node_ids(self):
602
612
  return self._find_all_objects(
@@ -320,7 +320,9 @@ class AppInstallationRepoAccess(BaseAsset):
320
320
  """One record from `app_installation_repo_access` → GH_CanAccess edge. No node."""
321
321
 
322
322
  installation_node_id: str
323
- repo_node_id: str
323
+ repository_database_id: int
324
+ repo_full_name: str | None = None
325
+ org_login: str
324
326
 
325
327
  @property
326
328
  def as_node(self):
@@ -328,11 +330,17 @@ class AppInstallationRepoAccess(BaseAsset):
328
330
 
329
331
  @property
330
332
  def edges(self) -> list[Edge]:
333
+ """Link the installation to a collected repository when it resolves."""
334
+ repo_node_id = self._lookup.repository_node_id_for_database_id(
335
+ self.repository_database_id, self.org_login
336
+ )
337
+ if not repo_node_id:
338
+ return []
331
339
  return [
332
340
  Edge(
333
341
  kind=ek.CAN_ACCESS,
334
342
  start=EdgePath(value=self.installation_node_id, match_by="id"),
335
- end=EdgePath(value=self.repo_node_id, match_by="id"),
343
+ end=EdgePath(value=repo_node_id, match_by="id"),
336
344
  properties=EdgeProperties(traversable=False),
337
345
  )
338
346
  ]
@@ -73,6 +73,14 @@ _CREDENTIAL_EXPORT_TIMEOUT_SECONDS = 300
73
73
  _CREDENTIAL_EXPORT_REUSE_SECONDS = 24 * 60 * 60
74
74
 
75
75
 
76
+ class _CredentialExportFailed(ValueError):
77
+ """The export job reported a terminal failed status."""
78
+
79
+
80
+ class _CredentialExportNotFound(requests.HTTPError):
81
+ """The export-status endpoint no longer recognizes a saved export ID."""
82
+
83
+
76
84
  @dataclass
77
85
  class SourceContext:
78
86
  """Shared context for GitHub API access."""
@@ -215,8 +223,13 @@ def _download_enterprise_credential_inventory(
215
223
  deadline = time.monotonic() + _CREDENTIAL_EXPORT_TIMEOUT_SECONDS
216
224
  export_path = f"{path}/{export_id}"
217
225
  while True:
218
- response = client.get(export_path, headers=headers, allow_redirects=False)
219
- response.raise_for_status()
226
+ try:
227
+ response = client.get(export_path, headers=headers, allow_redirects=False)
228
+ response.raise_for_status()
229
+ except requests.HTTPError as exc:
230
+ if exc.response is not None and exc.response.status_code == 404:
231
+ raise _CredentialExportNotFound(response=exc.response) from exc
232
+ raise
220
233
  if response.status_code == 302:
221
234
  download_url = response.headers.get("Location")
222
235
  parsed_url = urlparse(download_url or "")
@@ -259,6 +272,10 @@ def _download_enterprise_credential_inventory(
259
272
  polled_metadata = response.json()
260
273
  export_metadata.update(polled_metadata)
261
274
  status = polled_metadata.get("status")
275
+ if status == "failed":
276
+ raise _CredentialExportFailed(
277
+ "Credential export did not complete: 'failed'"
278
+ )
262
279
  if status not in {"queued", "started", "in_progress", "processing", "pending"}:
263
280
  raise ValueError(f"Credential export did not complete: {status!r}")
264
281
  if time.monotonic() >= deadline:
@@ -316,7 +333,7 @@ def enterprise_credential_inventory(enterprise_data: Enterprise, ctx: SourceCont
316
333
  }
317
334
  return
318
335
  except requests.HTTPError as exc:
319
- if exc.response is None or exc.response.status_code != 404:
336
+ if not isinstance(exc, _CredentialExportNotFound):
320
337
  logger.warning(
321
338
  "Skipping credential inventory for enterprise '%s': prior export %s unavailable (HTTP %s)",
322
339
  ctx.enterprise_name,
@@ -326,11 +343,20 @@ def enterprise_credential_inventory(enterprise_data: Enterprise, ctx: SourceCont
326
343
  else "unknown",
327
344
  )
328
345
  return
346
+ state.pop("last_export_id", None)
329
347
  logger.info(
330
348
  "Prior credential export %s is no longer available for enterprise '%s'; creating a new export",
331
349
  previous_id,
332
350
  ctx.enterprise_name,
333
351
  )
352
+ except _CredentialExportFailed:
353
+ state.pop("last_export_id", None)
354
+ logger.warning(
355
+ "Skipping credential inventory for enterprise '%s': prior export %s failed",
356
+ ctx.enterprise_name,
357
+ previous_id,
358
+ )
359
+ return
334
360
  except (
335
361
  requests.RequestException,
336
362
  ValueError,
@@ -23,6 +23,10 @@ from openhound_github.graphql import (
23
23
  TEAM_MEMBERS_OVERFLOW_QUERY,
24
24
  TEAMS_QUERY,
25
25
  )
26
+ from openhound_github.github_retry import (
27
+ is_primary_rate_limit_response,
28
+ is_secondary_rate_limit_response,
29
+ )
26
30
  from openhound_github.helpers import (
27
31
  AdaptiveGraphQLPageError,
28
32
  DEFAULT_GITHUB_REST_API_URL,
@@ -36,6 +40,7 @@ from openhound_github.models import (
36
40
  ActionPermission,
37
41
  App,
38
42
  AppInstallation,
43
+ AppInstallationRepoAccess,
39
44
  BaseRepoRole,
40
45
  Branch,
41
46
  BranchProtectionRule,
@@ -117,6 +122,7 @@ class SourceContext:
117
122
  deployment_type: str = "unknown"
118
123
  ghes_version: str | None = None
119
124
  enterprise_version_header: str | None = None
125
+ selected_installation_repo_access_stopped: bool = False
120
126
  cache_lock: Lock = field(default_factory=Lock)
121
127
  organizations_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
122
128
  app_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
@@ -655,6 +661,82 @@ def app_installations(ctx: SourceContext):
655
661
  continue
656
662
 
657
663
 
664
+ @app.transformer(
665
+ name="app_installation_repo_access",
666
+ columns=AppInstallationRepoAccess,
667
+ parallelized=False,
668
+ )
669
+ def app_installation_repo_access(app_install: AppInstallation, ctx: SourceContext):
670
+ """List the repositories granted to a selected organization installation."""
671
+ if (
672
+ app_install.repository_selection != "selected"
673
+ or not ctx.client
674
+ or not ctx.enterprise_name
675
+ or ctx.deployment_type == "ghes"
676
+ or ctx.selected_installation_repo_access_stopped
677
+ ):
678
+ return
679
+
680
+ path = (
681
+ f"/enterprises/{ctx.enterprise_name}/apps/organizations/"
682
+ f"{app_install.org_login}/installations/{app_install.id}/repositories"
683
+ )
684
+ try:
685
+ # Read every page before yielding so a failed page cannot create partial access.
686
+ repositories = [
687
+ repository
688
+ for page in ctx.client.paginate(
689
+ path,
690
+ params={"per_page": 100},
691
+ headers={"X-GitHub-Api-Version": "2026-03-10"},
692
+ )
693
+ for repository in page
694
+ ]
695
+ except requests.HTTPError as exc:
696
+ response = exc.response
697
+ if response is not None and (
698
+ response.status_code == 429
699
+ or is_primary_rate_limit_response(response)
700
+ or is_secondary_rate_limit_response(response)
701
+ or (response.status_code == 403 and response.headers.get("Retry-After"))
702
+ ):
703
+ ctx.selected_installation_repo_access_stopped = True
704
+ logger.warning(
705
+ "GitHub rate-limited selected app installation repository access; "
706
+ "skipping remaining installations in this collection"
707
+ )
708
+ return
709
+ logger.warning(
710
+ "Skipping repository access for app installation %s in organization '%s': %s",
711
+ app_install.id,
712
+ app_install.org_login,
713
+ exc,
714
+ )
715
+ return
716
+ except Exception as exc:
717
+ logger.warning(
718
+ "Skipping repository access for app installation %s in organization '%s': %s",
719
+ app_install.id,
720
+ app_install.org_login,
721
+ exc,
722
+ )
723
+ return
724
+
725
+ for repository in repositories:
726
+ if repository.get("id") is None:
727
+ logger.warning(
728
+ "Skipping repository without an id for app installation %s",
729
+ app_install.id,
730
+ )
731
+ continue
732
+ yield {
733
+ "installation_node_id": app_install.node_id,
734
+ "repository_database_id": repository["id"],
735
+ "repo_full_name": repository.get("full_name"),
736
+ "org_login": app_install.org_login,
737
+ }
738
+
739
+
658
740
  @app.transformer(name="applications", columns=App, parallelized=True)
659
741
  def applications(app_install: AppInstallation, ctx: SourceContext):
660
742
  """Fetch full GitHub App details for an installed app.
@@ -2368,6 +2450,7 @@ def org_scim_organizations(org: Organization, ctx: SourceContext):
2368
2450
 
2369
2451
 
2370
2452
  def organization_resources(ctx: SourceContext):
2453
+ """Assemble organization resources, including selected installation access."""
2371
2454
  org_resource = organizations(ctx)
2372
2455
  roles_resource = org_roles(ctx)
2373
2456
  repo_roles_base = RepositoryRoleCache(ctx)
@@ -2397,6 +2480,7 @@ def organization_resources(ctx: SourceContext):
2397
2480
  org_resource | roles_resource | org_role_teams(ctx),
2398
2481
  org_resource | roles_resource | org_role_members(ctx),
2399
2482
  app_installs_resource,
2483
+ app_installs_resource | app_installation_repo_access(ctx),
2400
2484
  app_installs_resource | applications(ctx),
2401
2485
  users(ctx),
2402
2486
  actions_permissions(ctx),
@@ -170,6 +170,7 @@ class SourceContext:
170
170
  deployment_type: str = "unknown"
171
171
  ghes_version: str | None = None
172
172
  enterprise_version_header: str | None = None
173
+ selected_installation_repo_access_stopped: bool = False
173
174
  cache_lock: Lock = field(default_factory=Lock)
174
175
  organizations_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
175
176
  app_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
@@ -0,0 +1,130 @@
1
+ from types import SimpleNamespace
2
+ from unittest.mock import MagicMock
3
+
4
+ import duckdb
5
+ from requests import HTTPError, Response
6
+
7
+ from openhound_github.lookup import GithubLookup
8
+ from openhound_github.models.app_installation import AppInstallationRepoAccess
9
+ from openhound_github.resources.organization import (
10
+ SourceContext,
11
+ app_installation_repo_access,
12
+ )
13
+
14
+
15
+ def _installation(selection="selected"):
16
+ """Build a minimal installation for repository access tests."""
17
+ return SimpleNamespace(
18
+ id=42,
19
+ node_id="GH_AppInstallation_42",
20
+ org_login="example-org",
21
+ repository_selection=selection,
22
+ )
23
+
24
+
25
+ def test_selected_installation_lists_all_pages_with_enterprise_client():
26
+ """Collect every page of repositories granted to a selected installation."""
27
+ client = MagicMock()
28
+ client.paginate.return_value = [
29
+ [{"id": 101, "name": "one", "full_name": "example-org/one"}],
30
+ [{"id": 102, "name": "two", "full_name": "example-org/two"}],
31
+ ]
32
+ ctx = SourceContext(
33
+ client=client, enterprise_name="enterprise", deployment_type="ghec"
34
+ )
35
+
36
+ rows = list(app_installation_repo_access.__wrapped__(_installation(), ctx))
37
+
38
+ assert rows == [
39
+ {
40
+ "installation_node_id": "GH_AppInstallation_42",
41
+ "repository_database_id": 101,
42
+ "repo_full_name": "example-org/one",
43
+ "org_login": "example-org",
44
+ },
45
+ {
46
+ "installation_node_id": "GH_AppInstallation_42",
47
+ "repository_database_id": 102,
48
+ "repo_full_name": "example-org/two",
49
+ "org_login": "example-org",
50
+ },
51
+ ]
52
+ client.paginate.assert_called_once_with(
53
+ "/enterprises/enterprise/apps/organizations/example-org/"
54
+ "installations/42/repositories",
55
+ params={"per_page": 100},
56
+ headers={"X-GitHub-Api-Version": "2026-03-10"},
57
+ )
58
+
59
+
60
+ def test_all_repositories_do_not_need_extra_request():
61
+ """Skip the repository lookup when an installation already selects all."""
62
+ client = MagicMock()
63
+ ctx = SourceContext(
64
+ client=client, enterprise_name="enterprise", deployment_type="ghec"
65
+ )
66
+
67
+ assert list(app_installation_repo_access.__wrapped__(_installation("all"), ctx)) == []
68
+ client.paginate.assert_not_called()
69
+
70
+
71
+ def test_failed_later_page_does_not_emit_partial_access(caplog):
72
+ """Discard earlier pages if a later page fails."""
73
+ client = MagicMock()
74
+
75
+ def pages(*args, **kwargs):
76
+ """Yield one page before simulating a pagination failure."""
77
+ yield [{"id": 101, "full_name": "example-org/one"}]
78
+ raise PermissionError("403 Forbidden")
79
+
80
+ client.paginate.side_effect = pages
81
+ ctx = SourceContext(
82
+ client=client, enterprise_name="enterprise", deployment_type="ghec"
83
+ )
84
+
85
+ assert list(app_installation_repo_access.__wrapped__(_installation(), ctx)) == []
86
+ assert "Skipping repository access" in caplog.text
87
+
88
+
89
+ def test_rate_limit_stops_remaining_selected_installation_requests(caplog):
90
+ """Stop further selected-installation lookups after a rate limit."""
91
+ response = Response()
92
+ response.status_code = 429
93
+ error = HTTPError("429 Too Many Requests", response=response)
94
+ client = MagicMock()
95
+ client.paginate.side_effect = error
96
+ ctx = SourceContext(
97
+ client=client, enterprise_name="enterprise", deployment_type="ghec"
98
+ )
99
+
100
+ assert list(app_installation_repo_access.__wrapped__(_installation(), ctx)) == []
101
+ assert list(app_installation_repo_access.__wrapped__(_installation(), ctx)) == []
102
+
103
+ client.paginate.assert_called_once()
104
+ assert ctx.selected_installation_repo_access_stopped is True
105
+ assert "skipping remaining installations" in caplog.text
106
+
107
+
108
+ def test_selected_repository_id_resolves_to_access_edge():
109
+ """Resolve repository access within the installation organization."""
110
+ connection = duckdb.connect(":memory:")
111
+ connection.execute("CREATE SCHEMA github")
112
+ connection.execute(
113
+ "CREATE TABLE github.repositories (node_id VARCHAR, database_id BIGINT, org_login VARCHAR)"
114
+ )
115
+ connection.execute(
116
+ "INSERT INTO github.repositories VALUES ('R_1', 101, 'example-org'), ('R_2', 101, 'other-org')"
117
+ )
118
+ access = AppInstallationRepoAccess(
119
+ installation_node_id="GH_AppInstallation_42",
120
+ repository_database_id=101,
121
+ org_login="example-org",
122
+ )
123
+ access._lookup = GithubLookup(connection)
124
+
125
+ edges = access.edges
126
+
127
+ assert len(edges) == 1
128
+ assert edges[0].start.value == "GH_AppInstallation_42"
129
+ assert edges[0].end.value == "R_1"
130
+ assert edges[0].properties.traversable is False
@@ -444,7 +444,10 @@ def test_classic_pat_inventory_resumes_export_after_download_failure(
444
444
  app_client.get.assert_not_called()
445
445
 
446
446
 
447
- def test_classic_pat_cached_export_error_does_not_log_download_url(monkeypatch, caplog):
447
+ @pytest.mark.parametrize("status_code", [404, 503])
448
+ def test_classic_pat_cached_export_error_does_not_log_download_url(
449
+ monkeypatch, caplog, status_code
450
+ ):
448
451
  """Keep signed download URLs out of cached-export error logs."""
449
452
  state = {
450
453
  "last_export_id": "prior-1",
@@ -460,7 +463,7 @@ def test_classic_pat_cached_export_error_does_not_log_download_url(monkeypatch,
460
463
  signed_url = "https://example.test/export.csv?secret=signed-download-token"
461
464
  client.get.return_value = Response(302, headers={"Location": signed_url})
462
465
  failed_response = requests.Response()
463
- failed_response.status_code = 503
466
+ failed_response.status_code = status_code
464
467
  failed_response.url = signed_url
465
468
  failed_response._content = b""
466
469
  monkeypatch.setattr(
@@ -477,8 +480,9 @@ def test_classic_pat_cached_export_error_does_not_log_download_url(monkeypatch,
477
480
  )
478
481
  == []
479
482
  )
480
- assert "HTTP 503" in caplog.text
483
+ assert f"HTTP {status_code}" in caplog.text
481
484
  assert "signed-download-token" not in caplog.text
485
+ assert state["last_export_id"] == "prior-1"
482
486
  client.post.assert_not_called()
483
487
 
484
488
 
@@ -596,13 +600,14 @@ def test_classic_pat_inventory_reuses_recent_export_before_creating(monkeypatch)
596
600
 
597
601
 
598
602
  def test_classic_pat_inventory_creates_export_when_recent_one_is_gone(monkeypatch):
603
+ state = {
604
+ "last_export_id": "prior-1",
605
+ "last_export_enterprise": "enterprise",
606
+ "last_export_downloaded_at": 1000,
607
+ }
599
608
  monkeypatch.setattr(
600
609
  "openhound_github.resources.enterprise.dlt.current.resource_state",
601
- lambda _: {
602
- "last_export_id": "prior-1",
603
- "last_export_enterprise": "enterprise",
604
- "last_export_downloaded_at": 1000,
605
- },
610
+ lambda _: state,
606
611
  )
607
612
  monkeypatch.setattr("openhound_github.resources.enterprise.time.time", lambda: 1001)
608
613
  client = MagicMock()
@@ -623,9 +628,68 @@ def test_classic_pat_inventory_creates_export_when_recent_one_is_gone(monkeypatc
623
628
  enterprise_credential_inventory.__wrapped__(SimpleNamespace(id="E_1"), ctx)
624
629
  )
625
630
  assert inventories[0]["export_id"] == "new-1"
631
+ assert state["last_export_id"] == "new-1"
626
632
  client.post.assert_called_once()
627
633
 
628
634
 
635
+ def test_classic_pat_inventory_discards_missing_prior_export(monkeypatch):
636
+ """Clear a missing export ID even if creating its replacement fails."""
637
+ state = {
638
+ "last_export_id": "prior-1",
639
+ "last_export_enterprise": "enterprise",
640
+ "last_export_created_at": 1000,
641
+ }
642
+ monkeypatch.setattr(
643
+ "openhound_github.resources.enterprise.dlt.current.resource_state",
644
+ lambda _: state,
645
+ )
646
+ monkeypatch.setattr("openhound_github.resources.enterprise.time.time", lambda: 1001)
647
+ client = MagicMock()
648
+ client.get.return_value = Response(404)
649
+ client.post.return_value = Response(429)
650
+ ctx = SourceContext(
651
+ client=client, enterprise_name="enterprise", deployment_type="ghec"
652
+ )
653
+
654
+ assert (
655
+ list(
656
+ enterprise_credential_inventory.__wrapped__(SimpleNamespace(id="E_1"), ctx)
657
+ )
658
+ == []
659
+ )
660
+ assert "last_export_id" not in state
661
+ client.post.assert_called_once()
662
+
663
+
664
+ @pytest.mark.parametrize("status", ["failed", "unknown"])
665
+ def test_classic_pat_inventory_only_discards_failed_prior_export(monkeypatch, status):
666
+ """Keep a saved ID for unexpected statuses but discard a failed export."""
667
+ state = {
668
+ "last_export_id": "prior-1",
669
+ "last_export_enterprise": "enterprise",
670
+ "last_export_created_at": 1000,
671
+ }
672
+ monkeypatch.setattr(
673
+ "openhound_github.resources.enterprise.dlt.current.resource_state",
674
+ lambda _: state,
675
+ )
676
+ monkeypatch.setattr("openhound_github.resources.enterprise.time.time", lambda: 1001)
677
+ client = MagicMock()
678
+ client.get.return_value = Response(200, payload={"status": status})
679
+ ctx = SourceContext(
680
+ client=client, enterprise_name="enterprise", deployment_type="ghec"
681
+ )
682
+
683
+ assert (
684
+ list(
685
+ enterprise_credential_inventory.__wrapped__(SimpleNamespace(id="E_1"), ctx)
686
+ )
687
+ == []
688
+ )
689
+ assert state.get("last_export_id") == (None if status == "failed" else "prior-1")
690
+ client.post.assert_not_called()
691
+
692
+
629
693
  def test_classic_pat_inventory_reuses_pat_created_export_with_pat(monkeypatch):
630
694
  state = {}
631
695
  monkeypatch.setattr(
@@ -747,7 +811,7 @@ def test_classic_pat_failed_export_produces_no_partial_inventory(caplog):
747
811
  )
748
812
  == []
749
813
  )
750
- assert "ValueError" in caplog.text
814
+ assert "CredentialExportFailed" in caplog.text
751
815
  client.post.assert_called_once()
752
816
 
753
817