Skip to content

Commit

Permalink
fix: writing jsonnet for embedded objects and dicts, team display
Browse files Browse the repository at this point in the history
  • Loading branch information
netomi committed Jan 1, 2025
1 parent 48feb52 commit b22a9d5
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 59 deletions.
14 changes: 11 additions & 3 deletions otterdog/models/github_organization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down Expand Up @@ -274,7 +274,9 @@ def from_model_data(cls, data: dict[str, Any]) -> GitHubOrganization:
"repositories": OptionalS("repositories", default=[]) >> Forall(lambda x: Repository.from_model_data(x)),
}

return cls(**bend(mapping, data))
org = cls(**bend(mapping, data))
org.repositories = [x.coerce_from_org_settings(org.settings) for x in org.repositories]
return org

def resolve_secrets(self, secret_resolver: Callable[[str], str]) -> None:
for webhook in self.webhooks:
Expand Down Expand Up @@ -344,12 +346,18 @@ def to_jsonnet(self, config: JsonnetConfig, context: PatchContext) -> str:

# print teams
if len(self.teams) > 0:
teams_by_name = associate_by_key(self.teams, lambda x: x.name)
default_teams_by_name = associate_by_key(default_org.teams, lambda x: x.name)

default_team = Team.from_model_data(config.default_team_config)

printer.println("teams+: [")
printer.level_up()

for team in self.teams:
for team_name, team in sorted(teams_by_name.items()):
if team_name in default_teams_by_name:
continue

team.to_jsonnet(printer, config, context, False, default_team)

printer.level_down()
Expand Down
14 changes: 12 additions & 2 deletions otterdog/models/organization_settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down Expand Up @@ -28,6 +28,7 @@
UNSET,
Change,
IndentingPrinter,
associate_by_key,
is_set_and_present,
is_set_and_valid,
unwrap,
Expand Down Expand Up @@ -190,14 +191,23 @@ def to_jsonnet(
extend: bool,
default_object: ModelObject,
) -> None:
default_org_settings = cast(OrganizationSettings, default_object)

patch = self.get_patch_to(default_object)
write_patch_object_as_json(patch, printer, False)

# print custom properties
if is_set_and_present(self.custom_properties) and len(self.custom_properties) > 0:
properties_by_name = associate_by_key(self.custom_properties, lambda x: x.name)
default_properties_by_name = associate_by_key(default_org_settings.custom_properties, lambda x: x.name)

for default_property_name in set(default_properties_by_name):
if default_property_name in properties_by_name:
properties_by_name.pop(default_property_name)

default_org_custom_property = CustomProperty.from_model_data(config.default_org_custom_property_config)

if len(self.custom_properties) > 0:
if len(properties_by_name) > 0:
printer.println("custom_properties+: [")
printer.level_up()

Expand Down
14 changes: 10 additions & 4 deletions otterdog/models/repository.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down Expand Up @@ -235,7 +235,7 @@ def add_environment(self, environment: Environment) -> None:
def set_environments(self, environments: list[Environment]) -> None:
self.environments = environments

def coerce_from_org_settings(self, org_settings: OrganizationSettings) -> Repository:
def coerce_from_org_settings(self, org_settings: OrganizationSettings, for_patch: bool = False) -> Repository:
copy = dataclasses.replace(self)

if org_settings.has_organization_projects is False:
Expand All @@ -247,11 +247,16 @@ def coerce_from_org_settings(self, org_settings: OrganizationSettings) -> Reposi
if is_set_and_present(self.custom_properties):
for custom_property in org_settings.custom_properties:
current_property_value = self.custom_properties.get(custom_property.name, None)
if current_property_value is None:
if current_property_value is None and for_patch is False:
if custom_property.required is True:
if custom_property.default_value is None:
raise ValueError("unexpected None value")
self.custom_properties[custom_property.name] = custom_property.default_value
elif current_property_value is not None and for_patch is True:
if custom_property.required is True:
if current_property_value == custom_property.default_value:
self.custom_properties.pop(custom_property.name)

return copy

def validate(self, context: ValidationContext, parent_object: Any) -> None:
Expand Down Expand Up @@ -864,7 +869,8 @@ def to_jsonnet(
extend: bool,
default_object: ModelObject,
) -> None:
patch = self.get_patch_to(default_object)
coerced_repo = self.coerce_from_org_settings(cast(OrganizationSettings, context.org_settings), for_patch=True)
patch = coerced_repo.get_patch_to(default_object)

has_webhooks = len(self.webhooks) > 0
has_secrets = len(self.secrets) > 0
Expand Down
56 changes: 31 additions & 25 deletions otterdog/models/ruleset.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down Expand Up @@ -750,14 +750,16 @@ def to_jsonnet(
embedded_extend = True

if is_set_and_valid(default_pull_request_config):
printer.print(f"required_pull_request{'+' if embedded_extend else ''}:")
self.required_pull_request.to_jsonnet(
printer,
jsonnet_config,
context,
embedded_extend,
default_pull_request_config,
)
patch = self.required_pull_request.get_patch_to(default_pull_request_config)
if len(patch) > 0:
printer.print(f"required_pull_request{'+' if embedded_extend else ''}:")
self.required_pull_request.to_jsonnet(
printer,
jsonnet_config,
context,
embedded_extend,
default_pull_request_config,
)

if is_set_and_present(self.required_merge_queue):
default_merge_queue_config = cast(Ruleset, default_object).required_merge_queue
Expand All @@ -770,14 +772,16 @@ def to_jsonnet(
embedded_extend = True

if is_set_and_valid(default_merge_queue_config):
printer.print(f"required_merge_queue{'+' if embedded_extend else ''}:")
self.required_merge_queue.to_jsonnet(
printer,
jsonnet_config,
context,
embedded_extend,
default_merge_queue_config,
)
patch = self.required_merge_queue.get_patch_to(default_merge_queue_config)
if len(patch) > 0:
printer.print(f"required_merge_queue{'+' if embedded_extend else ''}:")
self.required_merge_queue.to_jsonnet(
printer,
jsonnet_config,
context,
embedded_extend,
default_merge_queue_config,
)

if is_set_and_present(self.required_status_checks):
default_status_check_config = cast(Ruleset, default_object).required_status_checks
Expand All @@ -790,14 +794,16 @@ def to_jsonnet(
embedded_extend = True

if is_set_and_valid(default_status_check_config):
printer.print(f"required_status_checks{'+' if embedded_extend else ''}:")
self.required_status_checks.to_jsonnet(
printer,
jsonnet_config,
context,
embedded_extend,
default_status_check_config,
)
patch = self.required_status_checks.get_patch_to(default_status_check_config)
if len(patch) > 0:
printer.print(f"required_status_checks{'+' if embedded_extend else ''}:")
self.required_status_checks.to_jsonnet(
printer,
jsonnet_config,
context,
embedded_extend,
default_status_check_config,
)

# close the object
printer.level_down()
Expand Down
19 changes: 12 additions & 7 deletions otterdog/operations/diff_operation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down Expand Up @@ -101,23 +101,28 @@ async def execute(
self._org_config = org_config

self._print_project_header(org_config, org_index, org_count)
self.printer.level_up()

try:
return await self.generate_diff(org_config)
finally:
self.printer.level_down()

async def generate_diff(self, org_config: OrganizationConfig) -> int:
try:
self._gh_client = self.setup_github_client(org_config)
except RuntimeError as e:
self.printer.print_error(f"invalid credentials\n{e!s}")
return 1

self.printer.level_up()

try:
return await self.generate_diff(org_config)
return await self._generate_diff_internal(org_config)
except RuntimeError as e:
self.printer.print_error(f"planning aborted: {e!s}")
return 1
finally:
self.printer.level_down()
await self._gh_client.close()
if self._gh_client is not None:
await self._gh_client.close()

def setup_github_client(self, org_config: OrganizationConfig) -> GitHubProvider:
return GitHubProvider(self.get_credentials(org_config, only_token=self.no_web_ui))
Expand All @@ -135,7 +140,7 @@ def include_resources_with_secrets(self) -> bool:
def resolve_secrets(self) -> bool:
return True

async def generate_diff(self, org_config: OrganizationConfig) -> int:
async def _generate_diff_internal(self, org_config: OrganizationConfig) -> int:
github_id = org_config.github_id
jsonnet_config = org_config.jsonnet_config
await jsonnet_config.init_template()
Expand Down
2 changes: 1 addition & 1 deletion otterdog/operations/open_pull_request.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down
2 changes: 1 addition & 1 deletion otterdog/operations/push_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down
15 changes: 12 additions & 3 deletions otterdog/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# *******************************************************************************
# Copyright (c) 2023-2024 Eclipse Foundation and others.
# Copyright (c) 2023-2025 Eclipse Foundation and others.
# This program and the accompanying materials are made available
# under the terms of the Eclipse Public License 2.0
# which is available at http://www.eclipse.org/legal/epl-v20.html
Expand Down Expand Up @@ -131,7 +131,16 @@ def patch_to_other(value: Any, other_value: Any) -> tuple[bool, Any]:
else:
return False, None
else:
raise ValueError("non-empty dictionary values not supported yet")
diff = dict(value)

for k, v in other_value.items():
if diff.get(k) == v:
diff.pop(k)

if len(diff) == 0:
return False, None
else:
return True, diff
elif isinstance(value, list):
sorted_value_list = sorted(value)

Expand All @@ -141,7 +150,7 @@ def patch_to_other(value: Any, other_value: Any) -> tuple[bool, Any]:
sorted_other_list = sorted(other_value)

if sorted_value_list != sorted_other_list:
diff = _diff_list(sorted_value_list, sorted_other_list)
diff = _diff_list(sorted_value_list, sorted_other_list) # type: ignore
if len(diff) > 0:
return True, diff
else:
Expand Down
26 changes: 13 additions & 13 deletions otterdog/webapp/templates/home/organization.html
Original file line number Diff line number Diff line change
Expand Up @@ -417,24 +417,24 @@ <h3 class="card-title">Status</h3>
</div>
<div class="tab-pane" id="teams" role="tabpanel" aria-labelledby="teams-tab">
<div class="table-responsive p-0">
<table class="table table-hover text-nowrap">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Privacy</th>
<th>Notifications</th>
<th>Members</th>
<tr class="d-flex">
<th class="col-2">Name</th>
<th class="col-6">Description</th>
<th class="col-1">Privacy</th>
<th class="col-1">Notifications</th>
<th class="col-2">Members</th>
</tr>
</thead>
<tbody>
{% for team in config.teams|sort(attribute='name') %}
<tr>
<td>{{ team.name }}</td>
<td>{{ team.description }}</td>
<td>{{ team.privacy }}</td>
<td>{{ team.notifications }}</td>
<td>
<tr class="d-flex">
<td class="col-2">{{ team.name }}</td>
<td class="col-6" style="word-wrap: break-word;">{{ team.description }}</td>
<td class="col-1">{{ team.privacy }}</td>
<td class="col-1">{{ team.notifications }}</td>
<td class="col-2">
<ul>
{% for member in team.members %}
<li>{{ member }}</li>
Expand Down

0 comments on commit b22a9d5

Please sign in to comment.