Skip to content

Commit cd506b5

Browse files
tanwigeetika1618pre-commit-ci[bot]cidrblockssbarnea
authored
Add support for add subcommand to scaffold devfile resources (#305)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bradley A. Thornton <[email protected]> Co-authored-by: Sorin Sbarnea <[email protected]>
1 parent 49fee76 commit cd506b5

File tree

6 files changed

+467
-18
lines changed

6 files changed

+467
-18
lines changed

src/ansible_creator/arg_parser.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636

3737
COMING_SOON = (
3838
"add resource devcontainer",
39-
"add resource devfile",
4039
"add resource role",
4140
"add plugin action",
4241
"add plugin filter",
@@ -199,22 +198,7 @@ def _add_args_init_common(self, parser: ArgumentParser) -> None:
199198
"This flag is deprecated and will be removed soon."
200199
),
201200
)
202-
parser.add_argument(
203-
"-o",
204-
"--overwrite",
205-
default=False,
206-
dest="overwrite",
207-
action="store_true",
208-
help="Overwrite existing files or directories.",
209-
)
210-
parser.add_argument(
211-
"-no",
212-
"--no-overwrite",
213-
default=False,
214-
dest="no_overwrite",
215-
action="store_true",
216-
help="Flag that restricts overwriting operation.",
217-
)
201+
self._add_overwrite(parser)
218202

219203
def _add_args_plugin_common(self, parser: ArgumentParser) -> None:
220204
"""Add common plugin arguments to the parser.
@@ -293,6 +277,8 @@ def _add_resource_devfile(self: Parser, subparser: SubParser[ArgumentParser]) ->
293277
help="The destination directory for the devfile file. The default is the "
294278
"current working directory.",
295279
)
280+
281+
self._add_overwrite(parser)
296282
self._add_args_common(parser)
297283

298284
def _add_resource_role(self: Parser, subparser: SubParser[ArgumentParser]) -> None:
@@ -382,6 +368,29 @@ def _add_plugin_lookup(self: Parser, subparser: SubParser[ArgumentParser]) -> No
382368
self._add_args_common(parser)
383369
self._add_args_plugin_common(parser)
384370

371+
def _add_overwrite(self, parser: ArgumentParser) -> None:
372+
"""Add overwrite and no-overwrite arguments to the parser.
373+
374+
Args:
375+
parser: The parser to add overwrite and no_overwrite options
376+
"""
377+
parser.add_argument(
378+
"-o",
379+
"--overwrite",
380+
default=False,
381+
dest="overwrite",
382+
action="store_true",
383+
help="Overwrite existing files or directories.",
384+
)
385+
parser.add_argument(
386+
"-no",
387+
"--no-overwrite",
388+
default=False,
389+
dest="no_overwrite",
390+
action="store_true",
391+
help="Flag that restricts overwriting operation.",
392+
)
393+
385394
def _init(self: Parser, subparser: SubParser[ArgumentParser]) -> None:
386395
"""Initialize an Ansible project.
387396

src/ansible_creator/config.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ class Config:
3030
project: The type of project to scaffold.
3131
collection_name: The name of the collection.
3232
namespace: The namespace for the collection.
33+
resource_type: The type of resource to be scaffolded.
34+
type: The type of the project for which the resource is being scaffolded.
35+
path: The file path where the resource should be added.
3336
"""
3437

3538
creator_version: str
3639
output: Output
3740
subcommand: str
38-
3941
collection: str = ""
4042
force: bool = False
4143
overwrite: bool = False
@@ -44,6 +46,9 @@ class Config:
4446
project: str = ""
4547
collection_name: str | None = None
4648
namespace: str = ""
49+
resource_type: str = ""
50+
type: str = ""
51+
path: str = ""
4752

4853
def __post_init__(self: Config) -> None:
4954
"""Post process config values."""
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""Definitions for ansible-creator add action."""
2+
3+
from __future__ import annotations
4+
5+
import uuid
6+
7+
from pathlib import Path
8+
from typing import TYPE_CHECKING
9+
10+
from ansible_creator.exceptions import CreatorError
11+
from ansible_creator.templar import Templar
12+
from ansible_creator.types import TemplateData
13+
from ansible_creator.utils import Copier, Walker, ask_yes_no
14+
15+
16+
if TYPE_CHECKING:
17+
from ansible_creator.config import Config
18+
from ansible_creator.output import Output
19+
20+
21+
class Add:
22+
"""Class to handle the add subcommand."""
23+
24+
def __init__(
25+
self: Add,
26+
config: Config,
27+
) -> None:
28+
"""Initialize the add action.
29+
30+
Args:
31+
config: App configuration object.
32+
"""
33+
self._resource_type: str = config.resource_type
34+
self._resource_id: str = f"common.{self._resource_type}"
35+
self._add_path: Path = Path(config.path)
36+
self._force = config.force
37+
self._overwrite = config.overwrite
38+
self._no_overwrite = config.no_overwrite
39+
self._creator_version = config.creator_version
40+
self._project = config.project
41+
self.output: Output = config.output
42+
self.templar = Templar()
43+
44+
def run(self) -> None:
45+
"""Start scaffolding the resource file."""
46+
self._check_add_path()
47+
self.output.debug(msg=f"final collection path set to {self._add_path}")
48+
49+
self._scaffold()
50+
51+
def _check_add_path(self) -> None:
52+
"""Validate the provided add path.
53+
54+
Raises:
55+
CreatorError: If the add path does not exist.
56+
"""
57+
if not self._add_path.exists():
58+
msg = f"The path {self._add_path} does not exist. Please provide an existing directory."
59+
raise CreatorError(msg)
60+
61+
def unique_name_in_devfile(self) -> str:
62+
"""Use project specific name in devfile.
63+
64+
Returns:
65+
Unique name entry.
66+
"""
67+
final_name = ".".join(self._add_path.parts[-2:])
68+
final_uuid = str(uuid.uuid4())[:8]
69+
return f"{final_name}-{final_uuid}"
70+
71+
def _scaffold(self) -> None:
72+
"""Scaffold the specified resource file based on the resource type.
73+
74+
Raises:
75+
CreatorError: If unsupported resource type is given.
76+
"""
77+
self.output.debug(f"Started copying {self._project} resource to destination")
78+
79+
# Call the appropriate scaffolding function based on the resource type
80+
if self._resource_type == "devfile":
81+
template_data = self._get_devfile_template_data()
82+
83+
else:
84+
85+
msg = f"Unsupported resource type: {self._resource_type}"
86+
raise CreatorError(msg)
87+
88+
self._perform_scaffold(template_data)
89+
90+
def _perform_scaffold(self, template_data: TemplateData) -> None:
91+
"""Perform the actual scaffolding process using the provided template data.
92+
93+
Args:
94+
template_data: TemplateData
95+
96+
Raises:
97+
CreatorError: If there are conflicts and overwriting is not allowed, or if the
98+
destination directory contains files that will be overwritten.
99+
"""
100+
walker = Walker(
101+
resources=(f"common.{self._resource_type}",),
102+
resource_id=self._resource_id,
103+
dest=self._add_path,
104+
output=self.output,
105+
template_data=template_data,
106+
templar=self.templar,
107+
)
108+
paths = walker.collect_paths()
109+
copier = Copier(output=self.output)
110+
111+
if self._no_overwrite:
112+
msg = "The flag `--no-overwrite` restricts overwriting."
113+
if paths.has_conflicts():
114+
msg += (
115+
"\nThe destination directory contains files that can be overwritten."
116+
"\nPlease re-run ansible-creator with --overwrite to continue."
117+
)
118+
raise CreatorError(msg)
119+
120+
if not paths.has_conflicts() or self._force or self._overwrite:
121+
copier.copy_containers(paths)
122+
self.output.note(f"Resource added to {self._add_path}")
123+
return
124+
125+
if not self._overwrite:
126+
question = (
127+
"Files in the destination directory will be overwritten. Do you want to proceed?"
128+
)
129+
if ask_yes_no(question):
130+
copier.copy_containers(paths)
131+
else:
132+
msg = (
133+
"The destination directory contains files that will be overwritten."
134+
" Please re-run ansible-creator with --overwrite to continue."
135+
)
136+
raise CreatorError(msg)
137+
138+
self.output.note(f"Resource added to {self._add_path}")
139+
140+
def _get_devfile_template_data(self) -> TemplateData:
141+
"""Get the template data for devfile resources.
142+
143+
Returns:
144+
TemplateData: Data required for templating the devfile resource.
145+
"""
146+
return TemplateData(
147+
resource_type=self._resource_type,
148+
creator_version=self._creator_version,
149+
dev_file_name=self.unique_name_in_devfile(),
150+
)

src/ansible_creator/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class TemplateData:
1717
"""Dataclass representing the template data.
1818
1919
Attributes:
20+
resource_type: The type of resource to be scaffolded.
2021
additions: A dictionary containing additional data to add to the gitignore.
2122
collection_name: The name of the collection.
2223
creator_version: The version of the creator.
@@ -27,6 +28,7 @@ class TemplateData:
2728
recommended_extensions: A list of recommended VsCode extensions.
2829
"""
2930

31+
resource_type: str = ""
3032
additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict)
3133
collection_name: str = ""
3234
creator_version: str = ""
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
schemaVersion: 2.2.2
2+
metadata:
3+
name: testorg
4+
components:
5+
- name: tooling-container
6+
container:
7+
image: ghcr.io/ansible/ansible-workspace-env-reference:latest
8+
memoryRequest: 256M
9+
memoryLimit: 6Gi
10+
cpuRequest: 250m
11+
cpuLimit: 2000m
12+
args: ["tail", "-f", "/dev/null"]
13+
env:
14+
- name: KUBEDOCK_ENABLED
15+
value: "true"

0 commit comments

Comments
 (0)