Skip to content

Commit c6640f6

Browse files
committed
Merge remote-tracking branch 'origin/develop' into develop-1.1
2 parents 163b03d + 69160f3 commit c6640f6

File tree

153 files changed

+3612
-1190
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

153 files changed

+3612
-1190
lines changed

.vale/styles/Infrahub/sentence-case.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ exceptions:
4949
- JetStream
5050
- Jinja
5151
- Jinja2
52+
- JWT
5253
- Namespace
5354
- NATS
5455
- Node
@@ -63,9 +64,11 @@ exceptions:
6364
- RFile
6465
- SDK
6566
- Single sign-on
67+
- SSO
6668
- TLS
6769
- Tony Stark
6870
- TransformPython
71+
- UI
6972
- Vale
7073
- VS Code
7174
- VS Code extensions

.vale/styles/spelling-exceptions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ APIs
44
artifact_definitions
55
artifact_name
66
async
7+
Authentik
78
boolean
89
check_definitions
910
class_name

.yamllint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ignore: |
1010
**/node_modules
1111
# https://github.com/sbaudoin/yamllint/issues/16
1212
/helm/templates
13+
/python_sdk
1314
1415
rules:
1516
new-lines: disable

CHANGELOG.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,73 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang
1111

1212
<!-- towncrier release notes start -->
1313

14+
## [1.0.1](https://github.com/opsmill/infrahub/tree/v1.0.1) - 2024-10-31
15+
16+
### Fixed
17+
18+
- When a user is not logged in and the branch name is not found, hide the quick-create action and display the message: 'No branch found' ([#4801](https://github.com/opsmill/infrahub/issues/4801))
19+
- Fix automation to trigger generation of artifacts after merging a branch ([#4804](https://github.com/opsmill/infrahub/issues/4804))
20+
- Avoid sending an empty list to the load schema API on repository import if it's not required
21+
- Update demo environment to work with Infrahub 1.0
22+
23+
## [1.0.0](https://github.com/opsmill/infrahub/tree/v1.0.0) - 2024-10-30
24+
25+
### Removed
26+
27+
- Remove previously deprecated GET API endpoint "/api/schema/" ([#3884](https://github.com/opsmill/infrahub/issues/3884))
28+
29+
### Deprecated
30+
31+
- Marked CoreAccount.role as deprecated
32+
Due to the new permissions framework the account roles "admin" / "read-only" / "read-write" are deprecated and will be removed in Infrahub 1.1
33+
34+
### Added
35+
36+
- Reworked branch selector:
37+
- Redesigned the UI
38+
- Added filter for branch
39+
- Improved accessibility & keyboard navigation
40+
- Improved UX on new branch form
41+
- Added quick link to view all branches
42+
- Add support to sign in with OAuth2 and Open ID Connect (OIDC) ([#1568](https://github.com/opsmill/infrahub/issues/1568))
43+
- Add internal HTTP adapter to allow for generic access from Infrahub ([#3302](https://github.com/opsmill/infrahub/issues/3302))
44+
- Add support to search a node by human friendly ID within a GraphQL query ([#3908](https://github.com/opsmill/infrahub/issues/3908))
45+
- Added link to our Discord server in the account menu
46+
- Added permissions framework for global and object kind level permissions
47+
48+
In this first iteration the object permissions are applied to nodes as a whole, in upcoming versions it will be possible to define attribute level permissions as well.
49+
- New permissions system in UI:
50+
- Implemented CRUD views for managing accounts, groups, roles, and permissions
51+
- Updated all components to support new permission system
52+
- Added dynamic message display according to user access levels
53+
54+
### Fixed
55+
56+
- The `infrahub-git` agent service has been renamed to `task-worker` in docker compose and the command to start it has been updated as well ([#1075](https://github.com/opsmill/infrahub/issues/1075))
57+
- Add ability to import repositories with default branch other than 'main' ([#3435](https://github.com/opsmill/infrahub/issues/3435))
58+
- Disable approve/merge/close buttons for merged Proposed Changes ([#3495](https://github.com/opsmill/infrahub/issues/3495))
59+
- Fixed regex validation for List type attributes ([#3929](https://github.com/opsmill/infrahub/issues/3929))
60+
- Allow users to run artifacts and generators on nodes without name attribute ([#4062](https://github.com/opsmill/infrahub/issues/4062))
61+
- In the schema, properly delete inherited attribute and relationship on Node when the original attribute or relationship are being deleted on the Generic ([#4301](https://github.com/opsmill/infrahub/issues/4301))
62+
- "Retry All" button for checks is bigger ([#4315](https://github.com/opsmill/infrahub/issues/4315))
63+
- Add a size restriction on common attribute kinds. Only TextArea and JSON support large values ([#4432](https://github.com/opsmill/infrahub/issues/4432))
64+
- The HFID of a related node is properly returned via GraphQL in all scenarios ([#4482](https://github.com/opsmill/infrahub/issues/4482))
65+
- Add full validation to BranchMerge and BranchRebase mutations ([#4595](https://github.com/opsmill/infrahub/issues/4595))
66+
- Report user-friendly error for invalid uniqueness_constraints when loading schemas ([#4677](https://github.com/opsmill/infrahub/issues/4677))
67+
- Fixed pagination query for nodes with order_by clause using non unique attributes ([#4700](https://github.com/opsmill/infrahub/issues/4700))
68+
- Fixed schema migration when an attribute previously present on a node is added back ([#4727](https://github.com/opsmill/infrahub/issues/4727))
69+
- Add order_weight property to multiple attributes and relationships in the demo schema to improve how some models are displayed in the list views
70+
- Changed the Python SDK connection timeout to 60s
71+
- Fix metric missing the query name in Prometheus data
72+
- Fixes an issue where docker compose would output ANSI control characters that don't support it
73+
- Prevent temporary directories generated by Docusaurus to be imported by Docker
74+
75+
## [0.16.4](https://github.com/opsmill/infrahub/tree/v0.16.4) - 2024-10-17
76+
77+
### Fixed
78+
79+
- Fixed an issue on the UI where a new relationship was being added to the main branch instead of the current branch. ([#4598](https://github.com/opsmill/infrahub/issues/4598))
80+
1481
## [0.16.3](https://github.com/opsmill/infrahub/tree/v0.16.3) - 2024-10-10
1582

1683
### Removed

backend/infrahub/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,9 @@ class DatabaseSettings(BaseSettings):
197197
tls_insecure: bool = Field(default=False, description="Indicates if TLS certificates are verified")
198198
tls_ca_file: Optional[str] = Field(default=None, description="File path to CA cert or bundle in PEM format")
199199
query_size_limit: int = Field(
200-
default=5000,
200+
default=5_000,
201201
ge=1,
202-
le=20000,
202+
le=20_000,
203203
description="The max number of records to fetch in a single query before performing internal pagination.",
204204
)
205205
max_depth_search_hierarchy: int = Field(

backend/infrahub/core/attribute.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -396,12 +396,15 @@ async def _update(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) ->
396396
self.validate(value=self.value, name=self.name, schema=self.schema)
397397

398398
# Check if the current value is still the default one
399-
if (
400-
self.is_default
401-
and (self.schema.default_value is not None and self.schema.default_value != self.value)
402-
or (self.schema.default_value is None and self.value is not None)
403-
):
404-
self.is_default = False
399+
if self.is_default:
400+
if isinstance(self.value, Enum):
401+
has_default_value = self.schema.default_value == self.value.value
402+
else:
403+
has_default_value = self.schema.default_value == self.value
404+
if (self.schema.default_value is not None and not has_default_value) or (
405+
self.schema.default_value is None and self.value is not None
406+
):
407+
self.is_default = False
405408

406409
query = await NodeListGetAttributeQuery.init(
407410
db=db,

backend/infrahub/core/branch/tasks.py

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
from infrahub import lock
66
from infrahub.core import registry
77
from infrahub.core.branch import Branch
8+
from infrahub.core.diff.coordinator import DiffCoordinator
89
from infrahub.core.diff.ipam_diff_parser import IpamDiffParser
10+
from infrahub.core.diff.merger.merger import DiffMerger
11+
from infrahub.core.diff.repository.repository import DiffRepository
912
from infrahub.core.merge import BranchMerger
1013
from infrahub.core.migrations.schema.models import SchemaApplyMigrationData
1114
from infrahub.core.migrations.schema.tasks import schema_apply_migrations
15+
from infrahub.core.validators.determiner import ConstraintValidatorDeterminer
1216
from infrahub.core.validators.models.validate_migration import SchemaValidateMigrationData
1317
from infrahub.core.validators.tasks import schema_validate_migrations
18+
from infrahub.dependencies.registry import get_component_registry
1419
from infrahub.exceptions import ValidationError
1520
from infrahub.log import get_log_data
1621
from infrahub.message_bus import Meta, messages
@@ -27,15 +32,38 @@ async def rebase_branch(branch: str) -> None:
2732
await add_branch_tag(branch_name=branch)
2833

2934
obj = await Branch.get_by_name(db=service.database, name=branch)
30-
merger = BranchMerger(db=service.database, source_branch=obj, service=service)
35+
base_branch = await Branch.get_by_name(db=service.database, name=registry.default_branch)
36+
component_registry = get_component_registry()
37+
diff_coordinator = await component_registry.get_component(DiffCoordinator, db=service.database, branch=obj)
38+
diff_merger = await component_registry.get_component(DiffMerger, db=service.database, branch=obj)
39+
merger = BranchMerger(
40+
db=service.database,
41+
diff_coordinator=diff_coordinator,
42+
diff_merger=diff_merger,
43+
source_branch=obj,
44+
service=service,
45+
)
46+
diff_repository = await component_registry.get_component(DiffRepository, db=service.database, branch=obj)
47+
enriched_diff = await diff_coordinator.update_branch_diff(base_branch=base_branch, diff_branch=obj)
48+
if enriched_diff.get_all_conflicts():
49+
raise ValidationError(
50+
f"Branch {obj.name} contains conflicts with the default branch that must be addressed."
51+
" Please review the diff for details and manually update the conflicts before rebasing."
52+
)
53+
node_diff_field_summaries = await diff_repository.get_node_field_summaries(
54+
diff_branch_name=enriched_diff.diff_branch_name, diff_id=enriched_diff.uuid
55+
)
56+
57+
candidate_schema = merger.get_candidate_schema()
58+
determiner = ConstraintValidatorDeterminer(schema_branch=candidate_schema)
59+
constraints = await determiner.get_constraints(node_diffs=node_diff_field_summaries)
3160

3261
# If there are some changes related to the schema between this branch and main, we need to
33-
# - Run all the validations to ensure everything if correct before rebasing the branch
62+
# - Run all the validations to ensure everything is correct before rebasing the branch
3463
# - Run all the migrations after the rebase
3564
if obj.has_schema_changes:
36-
candidate_schema = merger.get_candidate_schema()
37-
constraints = await merger.calculate_validations(target_schema=candidate_schema)
38-
65+
constraints += await merger.calculate_validations(target_schema=candidate_schema)
66+
if constraints:
3967
error_messages = await schema_validate_migrations(
4068
message=SchemaValidateMigrationData(branch=obj, schema_branch=candidate_schema, constraints=constraints)
4169
)
@@ -44,25 +72,26 @@ async def rebase_branch(branch: str) -> None:
4472

4573
schema_in_main_before = merger.destination_schema.duplicate()
4674

47-
async with service.database.start_transaction() as dbt:
48-
await obj.rebase(db=dbt)
49-
log.info("Branch successfully rebased")
50-
51-
if obj.has_schema_changes:
52-
# NOTE there is a bit additional work in order to calculate a proper diff that will
53-
# allow us to pull only the part of the schema that has changed, for now the safest option is to pull
54-
# Everything
55-
# schema_diff = await merger.has_schema_changes()
56-
# TODO Would be good to convert this part to a Prefect Task in order to track it properly
57-
updated_schema = await registry.schema.load_schema_from_db(
58-
db=service.database,
59-
branch=obj,
60-
# schema=merger.source_schema.duplicate(),
61-
# schema_diff=schema_diff,
62-
)
63-
registry.schema.set_schema_branch(name=obj.name, schema=updated_schema)
64-
obj.update_schema_hash()
65-
await obj.save(db=service.database)
75+
async with lock.registry.global_graph_lock():
76+
async with service.database.start_transaction() as dbt:
77+
await obj.rebase(db=dbt)
78+
log.info("Branch successfully rebased")
79+
80+
if obj.has_schema_changes:
81+
# NOTE there is a bit additional work in order to calculate a proper diff that will
82+
# allow us to pull only the part of the schema that has changed, for now the safest option is to pull
83+
# Everything
84+
# schema_diff = await merger.has_schema_changes()
85+
# TODO Would be good to convert this part to a Prefect Task in order to track it properly
86+
updated_schema = await registry.schema.load_schema_from_db(
87+
db=service.database,
88+
branch=obj,
89+
# schema=merger.source_schema.duplicate(),
90+
# schema_diff=schema_diff,
91+
)
92+
registry.schema.set_schema_branch(name=obj.name, schema=updated_schema)
93+
obj.update_schema_hash()
94+
await obj.save(db=service.database)
6695

6796
# Execute the migrations
6897
migrations = await merger.calculate_migrations(target_schema=updated_schema)
@@ -116,11 +145,16 @@ async def merge_branch(branch: str, conflict_resolution: dict[str, bool] | None
116145
await add_branch_tag(branch_name=registry.default_branch)
117146

118147
obj = await Branch.get_by_name(db=service.database, name=branch)
148+
component_registry = get_component_registry()
119149

120150
merger: BranchMerger | None = None
121151
async with lock.registry.global_graph_lock():
122152
async with service.database.start_transaction() as db:
123-
merger = BranchMerger(db=db, source_branch=obj, service=service)
153+
diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=obj)
154+
diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=obj)
155+
merger = BranchMerger(
156+
db=db, diff_coordinator=diff_coordinator, diff_merger=diff_merger, source_branch=obj, service=service
157+
)
124158
await merger.merge(conflict_resolution=conflict_resolution)
125159
await merger.update_schema()
126160

backend/infrahub/core/diff/data_check_synchronizer.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from infrahub.core.manager import NodeManager
66
from infrahub.core.node import Node
77
from infrahub.database import InfrahubDatabase
8+
from infrahub.exceptions import SchemaNotFoundError
89

910
from .conflicts_extractor import DiffConflictsExtractor
1011
from .model.path import ConflictSelection, EnrichedDiffConflict, EnrichedDiffRoot
@@ -22,14 +23,18 @@ def __init__(
2223
self.conflict_recorder = conflict_recorder
2324

2425
async def synchronize(self, enriched_diff: EnrichedDiffRoot) -> list[Node]:
25-
proposed_changes = await NodeManager.query(
26-
db=self.db,
27-
schema=InfrahubKind.PROPOSEDCHANGE,
28-
filters={"source_branch": enriched_diff.diff_branch_name, "state": ProposedChangeState.OPEN},
29-
)
26+
try:
27+
proposed_changes = await NodeManager.query(
28+
db=self.db,
29+
schema=InfrahubKind.PROPOSEDCHANGE,
30+
filters={"source_branch": enriched_diff.diff_branch_name, "state": ProposedChangeState.OPEN},
31+
)
32+
except SchemaNotFoundError:
33+
# if the CoreProposedChange schema does not exist, then there's nothing to do
34+
proposed_changes = []
3035
if not proposed_changes:
3136
return []
32-
enriched_conflicts = enriched_diff.get_all_conflicts()
37+
enriched_conflicts_map = enriched_diff.get_all_conflicts()
3338
data_conflicts = await self.conflicts_extractor.get_data_conflicts(enriched_diff_root=enriched_diff)
3439
all_data_checks = []
3540
for pc in proposed_changes:
@@ -38,7 +43,7 @@ async def synchronize(self, enriched_diff: EnrichedDiffRoot) -> list[Node]:
3843
)
3944
all_data_checks.extend(core_data_checks)
4045
core_data_checks_by_id = {cdc.enriched_conflict_id.value: cdc for cdc in core_data_checks} # type: ignore[attr-defined]
41-
enriched_conflicts_by_id = {ec.uuid: ec for ec in enriched_conflicts}
46+
enriched_conflicts_by_id = {ec.uuid: ec for ec in enriched_conflicts_map.values()}
4247
for conflict_id, core_data_check in core_data_checks_by_id.items():
4348
enriched_conflict = enriched_conflicts_by_id.get(conflict_id)
4449
if not enriched_conflict:

0 commit comments

Comments
 (0)