Skip to content

Commit e42fdf3

Browse files
Add GitHub runner groups and org/repo runner collection (#5)
* Add GitHub runner groups and org/repo runner collection - model GH_RunnerGroup, GH_OrgRunner, and GH_RepoRunner nodes - add GH_CanUseRunner and runner membership edges - collect org runners from the org-level runners endpoint - collect runner group membership separately from runner inventory - capture repo self-hosted runner eligibility and org runner policy metadata * Changes to properties defaults + yielding edges instead of returning lists * Testing caching repos as a list to prevent duplicate API calls for runners * Removed commented code --------- Co-authored-by: Joey Dreijer <jdreijer@specterops.io>
1 parent 338b89b commit e42fdf3

7 files changed

Lines changed: 574 additions & 4 deletions

File tree

src/openhound_github/kinds/edges.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
# Access and capability edges
1515
CAN_ACCESS = "GH_CanAccess"
16+
CAN_USE_RUNNER = "GH_CanUseRunner"
1617
CAN_CREATE_BRANCH = "GH_CanCreateBranch"
1718
CAN_EDIT_PROTECTION = "GH_CanEditProtection"
1819
CAN_WRITE_BRANCH = "GH_CanWriteBranch"

src/openhound_github/kinds/nodes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@
3030
REPO_ROLE = "GH_RepoRole"
3131
REPO_SECRET = "GH_RepoSecret"
3232
REPO_VARIABLE = "GH_RepoVariable"
33+
REPO_RUNNER = "GH_RepoRunner"
3334

3435
DEFAULT_ROLE = "GH_RepoRole"
3536

37+
# Runner nodes
38+
RUNNER = "GH_Runner"
39+
RUNNER_GROUP = "GH_RunnerGroup"
40+
ORG_RUNNER = "GH_OrgRunner"
41+
3642
# Security nodes
3743
SECRET_SCANNING_ALERT = "GH_SecretScanningAlert"
3844

src/openhound_github/models/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from .repository_role import BaseRepoRole, RepoRole
2424
from .repository_secret import RepoSecret
2525
from .repository_variable import RepoVariable
26+
from .runner import OrgRunner, OrgRunnerGroupMembership, RepoRunner, RunnerGroup
2627
from .saml_provider import SamlProvider
2728
from .scim_user import ScimResource
2829
from .secret_scanning_alert import SecretScanningAlert
@@ -60,6 +61,10 @@
6061
"SelectedOrgVariable",
6162
"RepoSecret",
6263
"RepoVariable",
64+
"RunnerGroup",
65+
"OrgRunner",
66+
"OrgRunnerGroupMembership",
67+
"RepoRunner",
6368
"SecretScanningAlert",
6469
"AppInstallation",
6570
"BaseRepoRole",

src/openhound_github/models/org.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ class GHOrganizationProperties(GHNodeProperties):
264264
default=None,
265265
metadata={"description": "Whether SHA pinning is required for GitHub Actions."},
266266
)
267+
self_hosted_runners_enabled_repositories: str | None = field(
268+
default=None,
269+
metadata={
270+
"description": "Which repositories may use self-hosted runners: `all`, `selected`, or `none`."
271+
},
272+
)
267273
default_workflow_permissions: str | None = None
268274
can_approve_pull_request_reviews: bool | None = None
269275
query_organization_roles: str = ""
@@ -345,6 +351,7 @@ class Organization(BaseAsset):
345351
actions_enabled_repositories: str | None = None
346352
actions_allowed_actions: str | None = None
347353
actions_sha_pinning_required: bool | None = None
354+
self_hosted_runners_enabled_repositories: str | None = None
348355
default_workflow_permissions: str | None = None
349356
can_approve_pull_request_reviews: bool | None = None
350357

@@ -415,6 +422,7 @@ def as_node(self) -> GHNode:
415422
actions_enabled_repositories=self.actions_enabled_repositories,
416423
actions_allowed_actions=self.actions_allowed_actions,
417424
actions_sha_pinning_required=self.actions_sha_pinning_required,
425+
self_hosted_runners_enabled_repositories=self.self_hosted_runners_enabled_repositories,
418426
default_workflow_permissions=self.default_workflow_permissions,
419427
can_approve_pull_request_reviews=self.can_approve_pull_request_reviews,
420428
query_organization_roles=f"MATCH (:GH_Organization {{node_id:'{oid}'}})-[:GH_Contains]->(n:GH_OrgRole) RETURN n",

src/openhound_github/models/repository.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ class GHRepositoryProperties(GHNodeProperties):
9393
"description": "Whether GitHub Actions is enabled for this repository."
9494
},
9595
)
96+
self_hosted_runners_enabled: bool | None = field(
97+
default=None,
98+
metadata={
99+
"description": "Whether the repository may use self-hosted runners."
100+
},
101+
)
96102
secret_scanning: str | None = field(
97103
default=None,
98104
metadata={
@@ -105,6 +111,7 @@ class GHRepositoryProperties(GHNodeProperties):
105111
query_roles: str = ""
106112
query_teams: str = ""
107113
query_workflows: str = ""
114+
query_runners: str = ""
108115
query_environments: str = ""
109116
query_secrets: str = ""
110117
query_variables: str = ""
@@ -206,6 +213,8 @@ class Repository(BaseAsset):
206213
forks: int | None = None
207214
open_issues: int | None = None
208215
watchers: int | None = None
216+
actions_enabled: bool | None = None
217+
self_hosted_runners_enabled: bool | None = None
209218

210219
@property
211220
def owner_id(self) -> str:
@@ -245,14 +254,16 @@ def as_node(self) -> GHNode:
245254
owner_id=self.owner_id or "",
246255
environment_name=self._lookup.org_login(),
247256
environmentid=self._lookup.org_id(),
248-
# actions_enabled=self.actions_enabled,
257+
actions_enabled=self.actions_enabled,
258+
self_hosted_runners_enabled=self.self_hosted_runners_enabled,
249259
# secret_scanning=self.secret_scanning,
250260
query_branches=f"MATCH p=(:GH_Repository {{node_id: '{rid}'}})-[:GH_HasBranch]->(:GH_Branch) RETURN p",
251261
query_protected_branches=f"MATCH p=(:GH_Repository {{node_id: '{rid}'}})-[:GH_HasBranch]->(:GH_Branch)<-[:GH_ProtectedBy]-(:GH_BranchProtectionRule) RETURN p",
252262
query_branch_protection_rules=f"MATCH p=(:GH_Repository {{node_id: '{rid}'}})-[:GH_Contains]->(:GH_BranchProtectionRule) RETURN p",
253263
query_roles=f"MATCH p=(:GH_RepoRole)-[*1..]->(:GH_Repository {{node_id: '{rid}'}}) RETURN p",
254264
query_teams=f"MATCH p=(:GH_Team)-[:GH_MemberOf|GH_HasRole*1..]->(:GH_RepoRole)-[]->(:GH_Repository {{node_id: '{rid}'}}) RETURN p",
255265
query_workflows=f"MATCH p=(:GH_Repository {{node_id:'{rid}'}})-[:GH_HasWorkflow]->(w:GH_Workflow) RETURN p",
266+
query_runners=f"MATCH p=(:GH_Repository {{node_id:'{rid}'}})-[:GH_CanUseRunner]->(:GH_Runner) RETURN p",
256267
query_environments=f"MATCH p=(:GH_Repository {{node_id: '{rid}'}})-[:GH_HasEnvironment]->(:GH_Environment) RETURN p",
257268
query_secrets=f"MATCH p=(:GH_Repository {{node_id:'{rid}'}})-[:GH_HasSecret]->(:GH_Secret) RETURN p",
258269
query_variables=f"MATCH p=(:GH_Repository {{node_id:'{rid}'}})-[:GH_HasVariable]->(:GH_Variable) RETURN p",

0 commit comments

Comments
 (0)