infrahub-server 1.1.3__py3-none-any.whl → 1.1.5__py3-none-any.whl

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 (60) hide show
  1. infrahub/api/artifact.py +4 -1
  2. infrahub/api/oidc.py +32 -4
  3. infrahub/api/schema.py +16 -3
  4. infrahub/api/transformation.py +2 -0
  5. infrahub/computed_attribute/tasks.py +9 -5
  6. infrahub/config.py +0 -1
  7. infrahub/core/constants/__init__.py +5 -0
  8. infrahub/core/diff/calculator.py +118 -8
  9. infrahub/core/diff/coordinator.py +1 -2
  10. infrahub/core/diff/model/path.py +0 -9
  11. infrahub/core/diff/query/all_conflicts.py +64 -0
  12. infrahub/core/diff/query_parser.py +51 -28
  13. infrahub/core/diff/repository/repository.py +23 -14
  14. infrahub/core/merge.py +7 -5
  15. infrahub/core/query/diff.py +417 -621
  16. infrahub/core/relationship/model.py +10 -2
  17. infrahub/core/schema/__init__.py +5 -0
  18. infrahub/core/validators/aggregated_checker.py +34 -4
  19. infrahub/core/validators/node/hierarchy.py +1 -1
  20. infrahub/generators/tasks.py +12 -10
  21. infrahub/git/base.py +50 -8
  22. infrahub/git/integrator.py +24 -17
  23. infrahub/git/models.py +3 -2
  24. infrahub/git/repository.py +2 -2
  25. infrahub/git/tasks.py +19 -11
  26. infrahub/graphql/mutations/artifact_definition.py +10 -2
  27. infrahub/message_bus/messages/check_repository_usercheck.py +1 -0
  28. infrahub/message_bus/operations/check/repository.py +5 -2
  29. infrahub/message_bus/operations/requests/artifact_definition.py +1 -1
  30. infrahub/message_bus/operations/requests/proposed_change.py +4 -0
  31. infrahub/message_bus/types.py +1 -0
  32. infrahub/transformations/constants.py +1 -0
  33. infrahub/transformations/models.py +3 -1
  34. infrahub/transformations/tasks.py +2 -2
  35. infrahub/webhook/models.py +9 -1
  36. infrahub/workflows/catalogue.py +2 -2
  37. infrahub_sdk/analyzer.py +1 -1
  38. infrahub_sdk/checks.py +4 -4
  39. infrahub_sdk/client.py +26 -16
  40. infrahub_sdk/ctl/cli_commands.py +4 -4
  41. infrahub_sdk/ctl/exporter.py +2 -2
  42. infrahub_sdk/ctl/importer.py +6 -4
  43. infrahub_sdk/ctl/repository.py +56 -1
  44. infrahub_sdk/generator.py +3 -3
  45. infrahub_sdk/node.py +2 -2
  46. infrahub_sdk/pytest_plugin/items/base.py +0 -5
  47. infrahub_sdk/pytest_plugin/items/graphql_query.py +1 -1
  48. infrahub_sdk/pytest_plugin/items/jinja2_transform.py +1 -1
  49. infrahub_sdk/pytest_plugin/items/python_transform.py +1 -1
  50. infrahub_sdk/repository.py +33 -0
  51. infrahub_sdk/testing/repository.py +14 -8
  52. infrahub_sdk/transforms.py +3 -3
  53. infrahub_sdk/utils.py +8 -3
  54. {infrahub_server-1.1.3.dist-info → infrahub_server-1.1.5.dist-info}/METADATA +2 -1
  55. {infrahub_server-1.1.3.dist-info → infrahub_server-1.1.5.dist-info}/RECORD +59 -57
  56. infrahub_testcontainers/docker-compose.test.yml +1 -1
  57. infrahub_sdk/task_report.py +0 -208
  58. {infrahub_server-1.1.3.dist-info → infrahub_server-1.1.5.dist-info}/LICENSE.txt +0 -0
  59. {infrahub_server-1.1.3.dist-info → infrahub_server-1.1.5.dist-info}/WHEEL +0 -0
  60. {infrahub_server-1.1.3.dist-info → infrahub_server-1.1.5.dist-info}/entry_points.txt +0 -0
@@ -10,7 +10,8 @@ class TransformPythonData(BaseModel):
10
10
  data: dict = Field(..., description="Input data for the template")
11
11
  branch: str = Field(..., description="The branch to target")
12
12
  transform_location: str = Field(..., description="Location of the transform within the repository")
13
- commit: str = Field(..., description="The commit id to use when rendering the template")
13
+ commit: str = Field(..., description="The commit id to use when generating the artifact")
14
+ timeout: int = Field(..., description="The timeout value to use when generating the artifact")
14
15
 
15
16
 
16
17
  class TransformJinjaTemplateData(BaseModel):
@@ -23,3 +24,4 @@ class TransformJinjaTemplateData(BaseModel):
23
24
  branch: str = Field(..., description="The branch to target")
24
25
  template_location: str = Field(..., description="Location of the template within the repository")
25
26
  commit: str = Field(..., description="The commit id to use when rendering the template")
27
+ timeout: int = Field(..., description="The timeout value to use when rendering the template")
@@ -25,7 +25,7 @@ async def transform_python(message: TransformPythonData) -> Any:
25
25
  commit=message.commit,
26
26
  )
27
27
 
28
- transformed_data = await repo.execute_python_transform(
28
+ transformed_data = await repo.execute_python_transform.with_options(timeout_seconds=message.timeout)(
29
29
  branch_name=message.branch,
30
30
  commit=message.commit,
31
31
  location=message.transform_location,
@@ -49,7 +49,7 @@ async def transform_render_jinja2_template(message: TransformJinjaTemplateData)
49
49
  commit=message.commit,
50
50
  )
51
51
 
52
- rendered_template = await repo.render_jinja2_template(
52
+ rendered_template = await repo.render_jinja2_template.with_options(timeout_seconds=message.timeout)(
53
53
  commit=message.commit, location=message.template_location, data={"data": message.data}
54
54
  )
55
55
 
@@ -6,11 +6,13 @@ from math import floor
6
6
  from typing import Any, Optional, Union
7
7
  from uuid import uuid4
8
8
 
9
+ from infrahub_sdk.protocols import CoreTransformPython
9
10
  from pydantic import BaseModel, ConfigDict, Field
10
11
 
11
12
  from infrahub.core.constants import InfrahubKind
12
13
  from infrahub.git.repository import InfrahubReadOnlyRepository, InfrahubRepository
13
14
  from infrahub.services import InfrahubServices
15
+ from infrahub.transformations.constants import DEFAULT_TRANSFORM_TIMEOUT
14
16
 
15
17
 
16
18
  class SendWebhookData(BaseModel):
@@ -95,7 +97,13 @@ class TransformWebhook(Webhook):
95
97
  default_branch = repo.default_branch
96
98
  commit = repo.get_commit_value(branch_name=default_branch)
97
99
 
98
- self._payload = await repo.execute_python_transform(
100
+ timeout = DEFAULT_TRANSFORM_TIMEOUT
101
+ if transform := await self.service.client.get(
102
+ kind=CoreTransformPython, name__value=self.transform_name, raise_when_missing=False
103
+ ):
104
+ timeout = transform.timeout.value
105
+
106
+ self._payload = await repo.execute_python_transform.with_options(timeout_seconds=timeout)(
99
107
  branch_name=default_branch,
100
108
  commit=commit,
101
109
  location=f"{self.transform_file}::{self.transform_class}",
@@ -57,7 +57,7 @@ TRIGGER_ARTIFACT_DEFINITION_GENERATE = WorkflowDefinition(
57
57
  )
58
58
 
59
59
  TRIGGER_GENERATOR_DEFINITION_RUN = WorkflowDefinition(
60
- name="generator_definition_run",
60
+ name="generator-definition-run",
61
61
  type=WorkflowType.CORE,
62
62
  module="infrahub.generators.tasks",
63
63
  function="run_generator_definition",
@@ -81,7 +81,7 @@ REQUEST_GENERATOR_RUN = WorkflowDefinition(
81
81
  )
82
82
 
83
83
  REQUEST_GENERATOR_DEFINITION_RUN = WorkflowDefinition(
84
- name="request_generator_definition_run",
84
+ name="request-generator-definition-run",
85
85
  type=WorkflowType.CORE,
86
86
  module="infrahub.generators.tasks",
87
87
  function="request_generator_definition_run",
infrahub_sdk/analyzer.py CHANGED
@@ -91,7 +91,7 @@ class GraphQLQueryAnalyzer:
91
91
  else:
92
92
  data["default_value"] = variable.default_value.value
93
93
 
94
- if not data.get("default_value", None) and non_null:
94
+ if not data.get("default_value") and non_null:
95
95
  data["required"] = True
96
96
 
97
97
  response.append(GraphQLQueryVariable(**data))
infrahub_sdk/checks.py CHANGED
@@ -8,9 +8,10 @@ from abc import abstractmethod
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
10
10
  import ujson
11
- from git.repo import Repo
12
11
  from pydantic import BaseModel, Field
13
12
 
13
+ from infrahub_sdk.repository import GitRepoManager
14
+
14
15
  from .exceptions import UninitializedError
15
16
 
16
17
  if TYPE_CHECKING:
@@ -43,7 +44,7 @@ class InfrahubCheck:
43
44
  params: dict | None = None,
44
45
  client: InfrahubClient | None = None,
45
46
  ):
46
- self.git: Repo | None = None
47
+ self.git: GitRepoManager | None = None
47
48
  self.initializer = initializer or InfrahubCheckInitializer()
48
49
 
49
50
  self.logs: list[dict[str, Any]] = []
@@ -137,10 +138,9 @@ class InfrahubCheck:
137
138
  return self.branch
138
139
 
139
140
  if not self.git:
140
- self.git = Repo(self.root_directory)
141
+ self.git = GitRepoManager(self.root_directory)
141
142
 
142
143
  self.branch = str(self.git.active_branch)
143
-
144
144
  return self.branch
145
145
 
146
146
  @abstractmethod
infrahub_sdk/client.py CHANGED
@@ -547,8 +547,10 @@ class InfrahubClient(BaseClient):
547
547
  at: Timestamp | None = None,
548
548
  branch: str | None = None,
549
549
  timeout: int | None = None,
550
+ **kwargs: Any,
550
551
  ) -> int:
551
552
  """Return the number of nodes of a given kind."""
553
+ filters = kwargs
552
554
  schema = await self.schema.get(kind=kind, branch=branch)
553
555
 
554
556
  branch = branch or self.default_branch
@@ -556,7 +558,10 @@ class InfrahubClient(BaseClient):
556
558
  at = Timestamp(at)
557
559
 
558
560
  response = await self.execute_graphql(
559
- query=Query(query={schema.kind: {"count": None}}).render(), branch_name=branch, at=at, timeout=timeout
561
+ query=Query(query={schema.kind: {"count": None, "@filters": filters}}).render(),
562
+ branch_name=branch,
563
+ at=at,
564
+ timeout=timeout,
560
565
  )
561
566
  return int(response.get(schema.kind, {}).get("count", 0))
562
567
 
@@ -781,7 +786,7 @@ class InfrahubClient(BaseClient):
781
786
  nodes = []
782
787
  related_nodes = []
783
788
  batch_process = await self.create_batch()
784
- count = await self.count(kind=schema.kind)
789
+ count = await self.count(kind=schema.kind, **filters)
785
790
  total_pages = (count + pagination_size - 1) // pagination_size
786
791
 
787
792
  for page_number in range(1, total_pages + 1):
@@ -1181,7 +1186,7 @@ class InfrahubClient(BaseClient):
1181
1186
  async def allocate_next_ip_address(
1182
1187
  self,
1183
1188
  resource_pool: CoreNode,
1184
- kind: Literal[None] = ...,
1189
+ kind: None = ...,
1185
1190
  identifier: str | None = ...,
1186
1191
  prefix_length: int | None = ...,
1187
1192
  address_type: str | None = ...,
@@ -1196,7 +1201,7 @@ class InfrahubClient(BaseClient):
1196
1201
  async def allocate_next_ip_address(
1197
1202
  self,
1198
1203
  resource_pool: CoreNode,
1199
- kind: Literal[None] = ...,
1204
+ kind: None = ...,
1200
1205
  identifier: str | None = ...,
1201
1206
  prefix_length: int | None = ...,
1202
1207
  address_type: str | None = ...,
@@ -1211,7 +1216,7 @@ class InfrahubClient(BaseClient):
1211
1216
  async def allocate_next_ip_address(
1212
1217
  self,
1213
1218
  resource_pool: CoreNode,
1214
- kind: Literal[None] = ...,
1219
+ kind: None = ...,
1215
1220
  identifier: str | None = ...,
1216
1221
  prefix_length: int | None = ...,
1217
1222
  address_type: str | None = ...,
@@ -1328,7 +1333,7 @@ class InfrahubClient(BaseClient):
1328
1333
  async def allocate_next_ip_prefix(
1329
1334
  self,
1330
1335
  resource_pool: CoreNode,
1331
- kind: Literal[None] = ...,
1336
+ kind: None = ...,
1332
1337
  identifier: str | None = ...,
1333
1338
  prefix_length: int | None = ...,
1334
1339
  member_type: str | None = ...,
@@ -1344,7 +1349,7 @@ class InfrahubClient(BaseClient):
1344
1349
  async def allocate_next_ip_prefix(
1345
1350
  self,
1346
1351
  resource_pool: CoreNode,
1347
- kind: Literal[None] = ...,
1352
+ kind: None = ...,
1348
1353
  identifier: str | None = ...,
1349
1354
  prefix_length: int | None = ...,
1350
1355
  member_type: str | None = ...,
@@ -1360,7 +1365,7 @@ class InfrahubClient(BaseClient):
1360
1365
  async def allocate_next_ip_prefix(
1361
1366
  self,
1362
1367
  resource_pool: CoreNode,
1363
- kind: Literal[None] = ...,
1368
+ kind: None = ...,
1364
1369
  identifier: str | None = ...,
1365
1370
  prefix_length: int | None = ...,
1366
1371
  member_type: str | None = ...,
@@ -1651,8 +1656,10 @@ class InfrahubClientSync(BaseClient):
1651
1656
  at: Timestamp | None = None,
1652
1657
  branch: str | None = None,
1653
1658
  timeout: int | None = None,
1659
+ **kwargs: Any,
1654
1660
  ) -> int:
1655
1661
  """Return the number of nodes of a given kind."""
1662
+ filters = kwargs
1656
1663
  schema = self.schema.get(kind=kind, branch=branch)
1657
1664
 
1658
1665
  branch = branch or self.default_branch
@@ -1660,7 +1667,10 @@ class InfrahubClientSync(BaseClient):
1660
1667
  at = Timestamp(at)
1661
1668
 
1662
1669
  response = self.execute_graphql(
1663
- query=Query(query={schema.kind: {"count": None}}).render(), branch_name=branch, at=at, timeout=timeout
1670
+ query=Query(query={schema.kind: {"count": None, "@filters": filters}}).render(),
1671
+ branch_name=branch,
1672
+ at=at,
1673
+ timeout=timeout,
1664
1674
  )
1665
1675
  return int(response.get(schema.kind, {}).get("count", 0))
1666
1676
 
@@ -1920,7 +1930,7 @@ class InfrahubClientSync(BaseClient):
1920
1930
  related_nodes = []
1921
1931
  batch_process = self.create_batch()
1922
1932
 
1923
- count = self.count(kind=schema.kind)
1933
+ count = self.count(kind=schema.kind, **filters)
1924
1934
  total_pages = (count + pagination_size - 1) // pagination_size
1925
1935
 
1926
1936
  for page_number in range(1, total_pages + 1):
@@ -2296,7 +2306,7 @@ class InfrahubClientSync(BaseClient):
2296
2306
  def allocate_next_ip_address(
2297
2307
  self,
2298
2308
  resource_pool: CoreNodeSync,
2299
- kind: Literal[None] = ...,
2309
+ kind: None = ...,
2300
2310
  identifier: str | None = ...,
2301
2311
  prefix_length: int | None = ...,
2302
2312
  address_type: str | None = ...,
@@ -2311,7 +2321,7 @@ class InfrahubClientSync(BaseClient):
2311
2321
  def allocate_next_ip_address(
2312
2322
  self,
2313
2323
  resource_pool: CoreNodeSync,
2314
- kind: Literal[None] = ...,
2324
+ kind: None = ...,
2315
2325
  identifier: str | None = ...,
2316
2326
  prefix_length: int | None = ...,
2317
2327
  address_type: str | None = ...,
@@ -2326,7 +2336,7 @@ class InfrahubClientSync(BaseClient):
2326
2336
  def allocate_next_ip_address(
2327
2337
  self,
2328
2338
  resource_pool: CoreNodeSync,
2329
- kind: Literal[None] = ...,
2339
+ kind: None = ...,
2330
2340
  identifier: str | None = ...,
2331
2341
  prefix_length: int | None = ...,
2332
2342
  address_type: str | None = ...,
@@ -2439,7 +2449,7 @@ class InfrahubClientSync(BaseClient):
2439
2449
  def allocate_next_ip_prefix(
2440
2450
  self,
2441
2451
  resource_pool: CoreNodeSync,
2442
- kind: Literal[None] = ...,
2452
+ kind: None = ...,
2443
2453
  identifier: str | None = ...,
2444
2454
  prefix_length: int | None = ...,
2445
2455
  member_type: str | None = ...,
@@ -2455,7 +2465,7 @@ class InfrahubClientSync(BaseClient):
2455
2465
  def allocate_next_ip_prefix(
2456
2466
  self,
2457
2467
  resource_pool: CoreNodeSync,
2458
- kind: Literal[None] = ...,
2468
+ kind: None = ...,
2459
2469
  identifier: str | None = ...,
2460
2470
  prefix_length: int | None = ...,
2461
2471
  member_type: str | None = ...,
@@ -2471,7 +2481,7 @@ class InfrahubClientSync(BaseClient):
2471
2481
  def allocate_next_ip_prefix(
2472
2482
  self,
2473
2483
  resource_pool: CoreNodeSync,
2474
- kind: Literal[None] = ...,
2484
+ kind: None = ...,
2475
2485
  identifier: str | None = ...,
2476
2486
  prefix_length: int | None = ...,
2477
2487
  member_type: str | None = ...,
@@ -130,12 +130,12 @@ async def run(
130
130
  debug: bool = False,
131
131
  _: str = CONFIG_PARAM,
132
132
  branch: str = typer.Option("main", help="Branch on which to run the script."),
133
- concurrent: int = typer.Option(
134
- 4,
133
+ concurrent: int | None = typer.Option(
134
+ None,
135
135
  help="Maximum number of requests to execute at the same time.",
136
- envvar="INFRAHUBCTL_CONCURRENT_EXECUTION",
136
+ envvar="INFRAHUB_MAX_CONCURRENT_EXECUTION",
137
137
  ),
138
- timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUBCTL_TIMEOUT"),
138
+ timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUB_TIMEOUT"),
139
139
  variables: list[str] | None = typer.Argument(
140
140
  None, help="Variables to pass along with the query. Format key=value key=value."
141
141
  ),
@@ -26,9 +26,9 @@ def dump(
26
26
  concurrent: int = typer.Option(
27
27
  4,
28
28
  help="Maximum number of requests to execute at the same time.",
29
- envvar="INFRAHUBCTL_CONCURRENT_EXECUTION",
29
+ envvar="INFRAHUB_MAX_CONCURRENT_EXECUTION",
30
30
  ),
31
- timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUBCTL_TIMEOUT"),
31
+ timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUB_TIMEOUT"),
32
32
  exclude: list[str] = typer.Option(
33
33
  ["CoreAccount"],
34
34
  help="Prevent node kind(s) from being exported, CoreAccount is excluded by default",
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from asyncio import run as aiorun
2
4
  from pathlib import Path
3
5
 
@@ -24,12 +26,12 @@ def load(
24
26
  quiet: bool = typer.Option(False, help="No console output"),
25
27
  _: str = CONFIG_PARAM,
26
28
  branch: str = typer.Option("main", help="Branch from which to export"),
27
- concurrent: int = typer.Option(
28
- 4,
29
+ concurrent: int | None = typer.Option(
30
+ None,
29
31
  help="Maximum number of requests to execute at the same time.",
30
- envvar="INFRAHUBCTL_CONCURRENT_EXECUTION",
32
+ envvar="INFRAHUB_MAX_CONCURRENT_EXECUTION",
31
33
  ),
32
- timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUBCTL_TIMEOUT"),
34
+ timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUB_TIMEOUT"),
33
35
  ) -> None:
34
36
  """Import nodes and their relationships into the database."""
35
37
  console = Console()
@@ -6,13 +6,14 @@ import typer
6
6
  import yaml
7
7
  from pydantic import ValidationError
8
8
  from rich.console import Console
9
+ from rich.table import Table
9
10
 
10
11
  from infrahub_sdk.ctl.client import initialize_client
11
12
 
12
13
  from ..async_typer import AsyncTyper
13
14
  from ..ctl.exceptions import FileNotValidError
14
15
  from ..ctl.utils import init_logging
15
- from ..graphql import Mutation
16
+ from ..graphql import Mutation, Query
16
17
  from ..schema.repository import InfrahubRepositoryConfig
17
18
  from ._file import read_file
18
19
  from .parameters import CONFIG_PARAM
@@ -102,3 +103,57 @@ async def add(
102
103
  )
103
104
 
104
105
  await client.execute_graphql(query=query.render(), branch_name=branch, tracker="mutation-repository-create")
106
+
107
+
108
+ @app.command()
109
+ async def list(
110
+ branch: str | None = None,
111
+ debug: bool = False,
112
+ _: str = CONFIG_PARAM,
113
+ ) -> None:
114
+ init_logging(debug=debug)
115
+
116
+ client = initialize_client(branch=branch)
117
+
118
+ repo_status_query = {
119
+ "CoreGenericRepository": {
120
+ "edges": {
121
+ "node": {
122
+ "__typename": None,
123
+ "name": {"value": None},
124
+ "operational_status": {"value": None},
125
+ "sync_status": {"value": None},
126
+ "internal_status": {"value": None},
127
+ "... on CoreReadOnlyRepository": {
128
+ "ref": {"value": None},
129
+ },
130
+ }
131
+ }
132
+ },
133
+ }
134
+
135
+ query = Query(name="GetRepositoryStatus", query=repo_status_query)
136
+ resp = await client.execute_graphql(query=query.render(), branch_name=branch, tracker="query-repository-list")
137
+
138
+ table = Table(title="List of all Repositories")
139
+
140
+ table.add_column("Name", justify="right", style="cyan", no_wrap=True)
141
+ table.add_column("Type")
142
+ table.add_column("Operational status")
143
+ table.add_column("Sync status")
144
+ table.add_column("Internal status")
145
+ table.add_column("Ref")
146
+
147
+ for repository_node in resp["CoreGenericRepository"]["edges"]:
148
+ repository = repository_node["node"]
149
+
150
+ table.add_row(
151
+ repository["name"]["value"],
152
+ repository["__typename"],
153
+ repository["operational_status"]["value"],
154
+ repository["sync_status"]["value"],
155
+ repository["internal_status"]["value"],
156
+ repository["ref"]["value"] if "ref" in repository else "",
157
+ )
158
+
159
+ console.print(table)
infrahub_sdk/generator.py CHANGED
@@ -4,7 +4,7 @@ import os
4
4
  from abc import abstractmethod
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from git.repo import Repo
7
+ from infrahub_sdk.repository import GitRepoManager
8
8
 
9
9
  from .exceptions import UninitializedError
10
10
 
@@ -30,7 +30,7 @@ class InfrahubGenerator:
30
30
  ) -> None:
31
31
  self.query = query
32
32
  self.branch = branch
33
- self.git: Repo | None = None
33
+ self.git: GitRepoManager | None = None
34
34
  self.params = params or {}
35
35
  self.root_directory = root_directory or os.getcwd()
36
36
  self.generator_instance = generator_instance
@@ -81,7 +81,7 @@ class InfrahubGenerator:
81
81
  return self.branch
82
82
 
83
83
  if not self.git:
84
- self.git = Repo(self.root_directory)
84
+ self.git = GitRepoManager(self.root_directory)
85
85
 
86
86
  self.branch = str(self.git.active_branch)
87
87
 
infrahub_sdk/node.py CHANGED
@@ -1074,7 +1074,7 @@ class InfrahubNode(InfrahubNodeBase):
1074
1074
  timeout: int | None = None,
1075
1075
  ) -> Self:
1076
1076
  if not schema:
1077
- node_kind = data.get("__typename", None) or data.get("node", {}).get("__typename", None)
1077
+ node_kind = data.get("__typename") or data.get("node", {}).get("__typename", None)
1078
1078
  if not node_kind:
1079
1079
  raise ValueError("Unable to determine the type of the node, __typename not present in data")
1080
1080
  schema = await client.schema.get(kind=node_kind, branch=branch, timeout=timeout)
@@ -1594,7 +1594,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
1594
1594
  timeout: int | None = None,
1595
1595
  ) -> Self:
1596
1596
  if not schema:
1597
- node_kind = data.get("__typename", None) or data.get("node", {}).get("__typename", None)
1597
+ node_kind = data.get("__typename") or data.get("node", {}).get("__typename", None)
1598
1598
  if not node_kind:
1599
1599
  raise ValueError("Unable to determine the type of the node, __typename not present in data")
1600
1600
  schema = client.schema.get(kind=node_kind, branch=branch, timeout=timeout)
@@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  import pytest
8
8
  import ujson
9
- from git.exc import InvalidGitRepositoryError
10
9
 
11
10
  from ..exceptions import InvalidResourceConfigError
12
11
  from ..models import InfrahubInputOutputTest
@@ -28,7 +27,6 @@ class InfrahubItem(pytest.Item):
28
27
  **kwargs: dict[str, Any],
29
28
  ):
30
29
  super().__init__(*args, **kwargs) # type: ignore[arg-type]
31
-
32
30
  self.resource_name: str = resource_name
33
31
  self.resource_config: InfrahubRepositoryConfigElement = resource_config
34
32
  self.test: InfrahubTest = test
@@ -68,9 +66,6 @@ class InfrahubItem(pytest.Item):
68
66
  """Run the test logic."""
69
67
 
70
68
  def repr_failure(self, excinfo: pytest.ExceptionInfo, style: str | None = None) -> str: # noqa: ARG002
71
- if isinstance(excinfo.value, InvalidGitRepositoryError):
72
- return f"Invalid Git repository at {excinfo.value}"
73
-
74
69
  return str(excinfo.value)
75
70
 
76
71
  def reportinfo(self) -> tuple[Path | str, int | None, str]:
@@ -40,7 +40,7 @@ class InfrahubGraphQLQueryItem(InfrahubItem):
40
40
  )
41
41
 
42
42
  if isinstance(excinfo.value, OutputMatchError):
43
- return "\n".join([excinfo.value.message, excinfo.value.differences])
43
+ return f"{excinfo.value.message}\n{excinfo.value.differences}"
44
44
 
45
45
  return super().repr_failure(excinfo, style=style)
46
46
 
@@ -79,7 +79,7 @@ class InfrahubJinja2Item(InfrahubItem):
79
79
  return "\n".join(["Syntax error detected in the template", excinfo.value.message or ""])
80
80
 
81
81
  if isinstance(excinfo.value, OutputMatchError):
82
- return "\n".join([excinfo.value.message, excinfo.value.differences])
82
+ return f"{excinfo.value.message}\n{excinfo.value.differences}"
83
83
 
84
84
  return super().repr_failure(excinfo, style=style)
85
85
 
@@ -62,7 +62,7 @@ class InfrahubPythonTransformItem(InfrahubItem):
62
62
  )
63
63
 
64
64
  if isinstance(excinfo.value, OutputMatchError):
65
- return "\n".join([excinfo.value.message, excinfo.value.differences])
65
+ return f"{excinfo.value.message}\n{excinfo.value.differences}"
66
66
 
67
67
  return super().repr_failure(excinfo, style=style)
68
68
 
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from dulwich import porcelain
6
+ from dulwich.repo import Repo
7
+
8
+
9
+ class GitRepoManager:
10
+ def __init__(self, root_directory: str, branch: str = "main"):
11
+ self.root_directory = root_directory
12
+ self.branch = branch
13
+ self.git: Repo = self.initialize_repo()
14
+
15
+ def initialize_repo(self) -> Repo:
16
+ # Check if the directory already has a repository
17
+
18
+ root_path = Path(self.root_directory)
19
+
20
+ if root_path.exists() and (root_path / ".git").is_dir():
21
+ repo = Repo(self.root_directory) # Open existing repo
22
+ else:
23
+ repo = Repo.init(self.root_directory, default_branch=self.branch.encode("utf-8"))
24
+
25
+ if not repo:
26
+ raise ValueError("Failed to initialize or open a repository.")
27
+
28
+ return repo
29
+
30
+ @property
31
+ def active_branch(self) -> str | None:
32
+ active_branch = porcelain.active_branch(self.root_directory).decode("utf-8")
33
+ return active_branch
@@ -7,10 +7,11 @@ from enum import Enum
7
7
  from pathlib import Path
8
8
  from typing import TYPE_CHECKING
9
9
 
10
- from git.repo import Repo
10
+ from dulwich import porcelain
11
11
 
12
12
  from infrahub_sdk.graphql import Mutation
13
13
  from infrahub_sdk.protocols import CoreGenericRepository
14
+ from infrahub_sdk.repository import GitRepoManager
14
15
 
15
16
  if TYPE_CHECKING:
16
17
  from infrahub_sdk import InfrahubClient
@@ -37,14 +38,14 @@ class GitRepo:
37
38
 
38
39
  type: GitRepoType = GitRepoType.INTEGRATED
39
40
 
40
- _repo: Repo | None = None
41
+ _repo: GitRepoManager | None = None
41
42
  initial_branch: str = "main"
42
43
  directories_to_ignore: list[str] = field(default_factory=list)
43
44
  remote_directory_name: str = "/remote"
44
45
  _branches: list[str] = field(default_factory=list)
45
46
 
46
47
  @property
47
- def repo(self) -> Repo:
48
+ def repo(self) -> GitRepoManager:
48
49
  if self._repo:
49
50
  return self._repo
50
51
  raise ValueError("Repo hasn't been initialized yet")
@@ -62,12 +63,17 @@ class GitRepo:
62
63
  dst=self.dst_directory / self.name,
63
64
  ignore=shutil.ignore_patterns(".git"),
64
65
  )
65
- self._repo = Repo.init(self.dst_directory / self.name, initial_branch=self.initial_branch)
66
- for untracked in self.repo.untracked_files:
67
- self.repo.index.add(untracked)
68
- self.repo.index.commit("First commit")
69
66
 
70
- self.repo.git.checkout(self.initial_branch)
67
+ self._repo = GitRepoManager(str(Path(self.dst_directory / self.name)), branch=self.initial_branch)
68
+
69
+ files = list(
70
+ porcelain.get_untracked_paths(self._repo.git.path, self._repo.git.path, self._repo.git.open_index())
71
+ )
72
+ files_to_add = [str(Path(self._repo.git.path) / t) for t in files]
73
+ if files_to_add:
74
+ porcelain.add(repo=self._repo.git.path, paths=files_to_add)
75
+ porcelain.commit(repo=self._repo.git.path, message="First commit")
76
+ porcelain.checkout_branch(self._repo.git, self.initial_branch.encode("utf-8"))
71
77
 
72
78
  async def add_to_infrahub(self, client: InfrahubClient, branch: str | None = None) -> dict:
73
79
  input_data = {
@@ -5,7 +5,7 @@ import os
5
5
  from abc import abstractmethod
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
- from git import Repo
8
+ from infrahub_sdk.repository import GitRepoManager
9
9
 
10
10
  from .exceptions import UninitializedError
11
11
 
@@ -27,7 +27,7 @@ class InfrahubTransform:
27
27
  server_url: str = "",
28
28
  client: InfrahubClient | None = None,
29
29
  ):
30
- self.git: Repo
30
+ self.git: GitRepoManager
31
31
 
32
32
  self.branch = branch
33
33
  self.server_url = server_url or os.environ.get("INFRAHUB_URL", "http://127.0.0.1:8000")
@@ -56,7 +56,7 @@ class InfrahubTransform:
56
56
  return self.branch
57
57
 
58
58
  if not hasattr(self, "git") or not self.git:
59
- self.git = Repo(self.root_directory)
59
+ self.git = GitRepoManager(self.root_directory)
60
60
 
61
61
  self.branch = str(self.git.active_branch)
62
62