-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/719 add workflow update nox session #720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
842478e
41382f8
176b432
22894fe
3dd7570
5d8ee8a
c64b251
4aed04d
c0a5101
5578a95
e90bd81
29ff123
c46899d
c80027b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||
| from __future__ import annotations | ||||||
|
|
||||||
| import argparse | ||||||
|
|
||||||
| import nox | ||||||
| from nox import Session | ||||||
|
|
||||||
| from exasol.toolbox.util.workflows.workflow import ( | ||||||
| ALL, | ||||||
| WORKFLOW_NAMES, | ||||||
| update_selected_workflow, | ||||||
| ) | ||||||
| from noxconfig import PROJECT_CONFIG | ||||||
|
|
||||||
|
|
||||||
| def _create_parser() -> argparse.ArgumentParser: | ||||||
| parser = argparse.ArgumentParser( | ||||||
| prog="nox -s workflow:update", | ||||||
| usage="nox -s workflow:update -- [-h] --name", | ||||||
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, | ||||||
| ) | ||||||
|
|
||||||
| parser.add_argument( | ||||||
| "--name", # Changed to singular | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think as a source comment this isn't too valuable as
Could we move it to the changelog / release notes?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also propose renaming the command from Rationale
Additionally: If we want to force users to explicitly agree to generate / update / or overwrite all workflows we could have a cli option
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Today, I'm in the mood for radical proposals: Why would anyone want to update only specific workflows at all? If we ask this question, then we could simplify the Nox session even further. And if at all, then a user could as well specify the files rather than "names of workflows". And if the PTB doesn't know how to update a specified file (because there is no related template), then the PTB could simply display an error message. Aside of that, I think, when updating "all" workflows, the PTB should also report if there is a specific workflow file unknown, ignored, and not updated. This could be important information for a user to identify potentially obsolete files. |
||||||
| default=ALL, | ||||||
| choices=WORKFLOW_NAMES, | ||||||
| help="Select one template by name or 'all' to update everything.", | ||||||
| required=True, | ||||||
| ) | ||||||
| return parser | ||||||
|
|
||||||
|
|
||||||
| @nox.session(name="workflow:update", python=False) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| def update_workflow(session: Session) -> None: | ||||||
| """ | ||||||
| Update (or install if it's not yet existing) one or all generated GitHub workflow(s) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| """ | ||||||
| parser = _create_parser() | ||||||
| args = parser.parse_args(session.posargs) | ||||||
|
|
||||||
| # Ensure that the GitHub workflow directory exists | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| PROJECT_CONFIG.github_workflow_directory.mkdir(parents=True, exist_ok=True) | ||||||
|
|
||||||
| update_selected_workflow(workflow_name=args.name, config=PROJECT_CONFIG) | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -33,7 +33,7 @@ def render(self) -> str: | |||||
| workflow_dict = self.get_yaml_dict() | ||||||
|
|
||||||
| if self.patch_yaml: | ||||||
| logger.debug("Modify workflow custom yaml") | ||||||
| logger.debug("Modify workflow with custom yaml") | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| workflow_modifier = WorkflowModifier( | ||||||
| workflow_dict=workflow_dict, patch_yaml=self.patch_yaml | ||||||
| ) | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,10 @@ | ||||||||||||||||||||||||||||||||
| from collections.abc import Mapping | ||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||||||||||||||
| from typing import ( | ||||||||||||||||||||||||||||||||
| Annotated, | ||||||||||||||||||||||||||||||||
| Any, | ||||||||||||||||||||||||||||||||
| Final, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| from pydantic import ( | ||||||||||||||||||||||||||||||||
| BaseModel, | ||||||||||||||||||||||||||||||||
|
|
@@ -9,9 +14,24 @@ | |||||||||||||||||||||||||||||||
| bound_contextvars, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| from exasol.toolbox.config import BaseConfig | ||||||||||||||||||||||||||||||||
| from exasol.toolbox.util.workflows import logger | ||||||||||||||||||||||||||||||||
| from exasol.toolbox.util.workflows.exceptions import YamlError | ||||||||||||||||||||||||||||||||
| from exasol.toolbox.util.workflows.exceptions import ( | ||||||||||||||||||||||||||||||||
| InvalidWorkflowPatcherEntryError, | ||||||||||||||||||||||||||||||||
| YamlError, | ||||||||||||||||||||||||||||||||
| YamlKeyError, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| from exasol.toolbox.util.workflows.patch_workflow import ( | ||||||||||||||||||||||||||||||||
| WorkflowCommentedMap, | ||||||||||||||||||||||||||||||||
| WorkflowPatcher, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| from exasol.toolbox.util.workflows.process_template import WorkflowRenderer | ||||||||||||||||||||||||||||||||
| from exasol.toolbox.util.workflows.templates import WORKFLOW_TEMPLATE_OPTIONS | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ALL: Final[str] = "all" | ||||||||||||||||||||||||||||||||
| WORKFLOW_NAMES: Final[list[str]] = [ALL, *WORKFLOW_TEMPLATE_OPTIONS.keys()] | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| WorkflowName = Annotated[str, f"Should be a value from {WORKFLOW_NAMES}"] | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| class Workflow(BaseModel): | ||||||||||||||||||||||||||||||||
|
|
@@ -20,9 +40,14 @@ class Workflow(BaseModel): | |||||||||||||||||||||||||||||||
| content: str | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @classmethod | ||||||||||||||||||||||||||||||||
| def load_from_template(cls, file_path: Path, github_template_dict: dict[str, Any]): | ||||||||||||||||||||||||||||||||
| def load_from_template( | ||||||||||||||||||||||||||||||||
| cls, | ||||||||||||||||||||||||||||||||
| file_path: Path, | ||||||||||||||||||||||||||||||||
| github_template_dict: dict[str, Any], | ||||||||||||||||||||||||||||||||
| patch_yaml: WorkflowCommentedMap | None = None, | ||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||
| with bound_contextvars(template_file_name=file_path.name): | ||||||||||||||||||||||||||||||||
| logger.info("Load workflow from template") | ||||||||||||||||||||||||||||||||
| logger.info(f"Load workflow template: {file_path.name}") | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if not file_path.exists(): | ||||||||||||||||||||||||||||||||
| raise FileNotFoundError(file_path) | ||||||||||||||||||||||||||||||||
|
|
@@ -31,12 +56,60 @@ def load_from_template(cls, file_path: Path, github_template_dict: dict[str, Any | |||||||||||||||||||||||||||||||
| workflow_renderer = WorkflowRenderer( | ||||||||||||||||||||||||||||||||
| github_template_dict=github_template_dict, | ||||||||||||||||||||||||||||||||
| file_path=file_path, | ||||||||||||||||||||||||||||||||
| patch_yaml=None, | ||||||||||||||||||||||||||||||||
| patch_yaml=patch_yaml, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| workflow = workflow_renderer.render() | ||||||||||||||||||||||||||||||||
| return cls(content=workflow) | ||||||||||||||||||||||||||||||||
| except YamlError as ex: | ||||||||||||||||||||||||||||||||
| except (YamlError, YamlKeyError) as ex: | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
| raise ex | ||||||||||||||||||||||||||||||||
| except Exception as ex: | ||||||||||||||||||||||||||||||||
| # Wrap all other "non-special" exceptions | ||||||||||||||||||||||||||||||||
| raise ValueError(f"Error rendering file: {file_path}") from ex | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def write_to_file(self, file_path: Path) -> None: | ||||||||||||||||||||||||||||||||
| logger.info(f"Write out workflow: {file_path.name}", file_path=file_path) | ||||||||||||||||||||||||||||||||
| file_path.write_text(self.content + "\n") | ||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+71
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def _select_workflow_template(workflow_name: WorkflowName) -> Mapping[str, Path]: | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| Returns a mapping of a workflow template or of all workflow templates. | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| if workflow_name == ALL: | ||||||||||||||||||||||||||||||||
| return WORKFLOW_TEMPLATE_OPTIONS | ||||||||||||||||||||||||||||||||
| return {workflow_name: WORKFLOW_TEMPLATE_OPTIONS[workflow_name]} | ||||||||||||||||||||||||||||||||
|
Comment on lines
+74
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the more general use case is plural, in rare cases n can be 1. I also doubt whether indexing the workflow paths by name (uniqueness comes from the OS file system and is based on the assumption that all files are in the same directory.) makes sense.
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def update_selected_workflow(workflow_name: WorkflowName, config: BaseConfig) -> None: | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| Updates a selected workflow or all workflows. | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| workflow_dict = _select_workflow_template(workflow_name) | ||||||||||||||||||||||||||||||||
| logger.info(f"Selected workflow(s) to update: {list(workflow_dict.keys())}") | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| workflow_patcher = None | ||||||||||||||||||||||||||||||||
| if config.github_workflow_patcher_yaml: | ||||||||||||||||||||||||||||||||
| workflow_patcher = WorkflowPatcher( | ||||||||||||||||||||||||||||||||
| github_template_dict=config.github_template_dict, | ||||||||||||||||||||||||||||||||
| file_path=config.github_workflow_patcher_yaml, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| for workflow_name in workflow_dict: | ||||||||||||||||||||||||||||||||
| patch_yaml = None | ||||||||||||||||||||||||||||||||
| if workflow_patcher: | ||||||||||||||||||||||||||||||||
| patch_yaml = workflow_patcher.extract_by_workflow( | ||||||||||||||||||||||||||||||||
| workflow_name=workflow_name | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||
| workflow = Workflow.load_from_template( | ||||||||||||||||||||||||||||||||
| file_path=workflow_dict[workflow_name], | ||||||||||||||||||||||||||||||||
| github_template_dict=config.github_template_dict, | ||||||||||||||||||||||||||||||||
| patch_yaml=patch_yaml, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| file_path = config.github_workflow_directory / f"{workflow_name}.yml" | ||||||||||||||||||||||||||||||||
| workflow.write_to_file(file_path=file_path) | ||||||||||||||||||||||||||||||||
| except YamlKeyError as ex: | ||||||||||||||||||||||||||||||||
| raise InvalidWorkflowPatcherEntryError( | ||||||||||||||||||||||||||||||||
| file_path=config.github_workflow_patcher_yaml, entry=ex.entry # type: ignore | ||||||||||||||||||||||||||||||||
| ) from ex | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.