pace-dotnet 0.1.0__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.
pace/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """PACE - Project Automation and Configuration Engine
2
+
3
+ A Python CLI tool for bulk management of C# .NET project ecosystems.
4
+ """
5
+
6
+ __version__ = "0.1.0"
pace/cli.py ADDED
@@ -0,0 +1,339 @@
1
+ """CLI entry point for PACE."""
2
+
3
+ import argparse
4
+ import os
5
+ import sys
6
+ from argparse import Namespace
7
+ from importlib.resources import files
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ # Force UTF-8 encoding for stdout/stderr to avoid encoding issues on Windows
12
+ os.environ["PYTHONIOENCODING"] = "utf-8:replace"
13
+ if sys.platform == "win32":
14
+ # Reconfigure stdout/stderr to use UTF-8 with replace error handling
15
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined]
16
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined]
17
+
18
+ import rich
19
+ from rich.console import Console
20
+ from rich.traceback import install
21
+
22
+ from pace.commands import clean, dotnet, git, upload
23
+ from pace.config import Config, load_config
24
+ from pace.rich_demos import columns, progress_bar
25
+
26
+ _DEMOS = {
27
+ "columns": columns.run,
28
+ "progress_bar": progress_bar.run,
29
+ }
30
+
31
+
32
+ class _Option:
33
+ def __init__(self, short: str, long: str, description: str, metavar: str | None = None) -> None:
34
+ self.short = short
35
+ self.long = long
36
+ self.metavar = metavar
37
+ self.description = description
38
+
39
+
40
+ class _Options:
41
+ DEBUG = _Option("", "--debug", "Enable debug mode with full tracebacks")
42
+ CONFIG = _Option(
43
+ "-C",
44
+ "--config",
45
+ "Path to configuration file. Defaults to an internal pace.toml file.",
46
+ metavar="<path>",
47
+ )
48
+ PRINT_CONFIG = _Option("", "--print-config", "Print the configuration and exit")
49
+ PRINT_CONFIG_PATH = _Option(
50
+ "", "--print-config-path", "Print the path to the configuration file and exit"
51
+ )
52
+ VERSION = _Option("-v", "--version", "Print the version and exit")
53
+ FROM_REPO = _Option(
54
+ "",
55
+ "--from",
56
+ "Starting repository name. Only projects in the dependency chain from this repo will be included.",
57
+ metavar="<reponame>",
58
+ )
59
+ TO_REPO = _Option(
60
+ "",
61
+ "--to",
62
+ "Ending repository name. Only projects in the dependency chain up to this repo will be included.",
63
+ metavar="<reponame>",
64
+ )
65
+
66
+
67
+ class PaceFormatter(argparse.HelpFormatter):
68
+ """Custom help formatter for PACE CLI."""
69
+
70
+ def _format_usage(self, usage: Any, actions: Any, groups: Any, prefix: Any) -> str: # noqa: ARG002
71
+ """Override usage formatting to match the desired style."""
72
+ return f"usage: [-h] [{_Options.CONFIG.short} {_Options.CONFIG.metavar}] [OPTIONS] command...\n\n"
73
+
74
+
75
+ def main() -> int:
76
+ """Main entry point for the PACE CLI.
77
+
78
+ Returns:
79
+ Exit code (0 for success, non-zero for failure)
80
+ """
81
+ console = Console()
82
+
83
+ parser = argparse.ArgumentParser(
84
+ description="PACE - Project Automation and Configuration Engine",
85
+ formatter_class=PaceFormatter,
86
+ )
87
+
88
+ parser.add_argument(
89
+ _Options.DEBUG.long,
90
+ action="store_true",
91
+ default=False,
92
+ help="Enable debug mode with full tracebacks",
93
+ )
94
+ parser.add_argument(
95
+ _Options.CONFIG.short,
96
+ _Options.CONFIG.long,
97
+ metavar=_Options.CONFIG.metavar,
98
+ help="Path to configuration file. Defaults to an internal pace.toml file.",
99
+ type=Path,
100
+ )
101
+ parser.add_argument(
102
+ _Options.PRINT_CONFIG.long,
103
+ action="store_true",
104
+ help="Print the loaded configuration",
105
+ default=False,
106
+ )
107
+ parser.add_argument(
108
+ _Options.PRINT_CONFIG_PATH.long,
109
+ action="store_true",
110
+ help="Print the path to the configuration file and exit",
111
+ default=False,
112
+ )
113
+ parser.add_argument(_Options.VERSION.long, action="version", version="pace 0.1.0")
114
+ parser.add_argument(
115
+ _Options.FROM_REPO.long,
116
+ dest="from_repo",
117
+ metavar=_Options.FROM_REPO.metavar,
118
+ help=_Options.FROM_REPO.description,
119
+ default=None,
120
+ )
121
+ parser.add_argument(
122
+ _Options.TO_REPO.long,
123
+ dest="to_repo",
124
+ metavar=_Options.TO_REPO.metavar,
125
+ help=_Options.TO_REPO.description,
126
+ default=None,
127
+ )
128
+
129
+ subparsers = parser.add_subparsers(dest="command", metavar="command")
130
+
131
+ clean_parser = subparsers.add_parser(
132
+ "clean", help="Delete build artifacts and NuGet cache for all projects"
133
+ )
134
+ clean_parser.add_argument(
135
+ "--cache",
136
+ action="store_true",
137
+ help="Clean NuGet packages from ~/.nuget/packages",
138
+ default=False,
139
+ )
140
+ clean_parser.add_argument(
141
+ "--custom-cache",
142
+ action="store_true",
143
+ help="Clean NuGet packages from the custom cache path configured in nuget_cache_path",
144
+ default=False,
145
+ )
146
+ clean_parser.add_argument(
147
+ "--project",
148
+ action="store_true",
149
+ help="Clean project bin/ and obj/ directories",
150
+ default=False,
151
+ )
152
+ clean_parser.add_argument(
153
+ "-n",
154
+ "--dry-run",
155
+ action="store_true",
156
+ help="Show what would be deleted without actually deleting",
157
+ default=False,
158
+ )
159
+ dotnet_parser = subparsers.add_parser(
160
+ "dotnet", help="Execute dotnet commands across the project graph"
161
+ )
162
+ dotnet_parser.add_argument(
163
+ "dotnet_args",
164
+ metavar="... <dotnet-args>",
165
+ nargs=argparse.REMAINDER,
166
+ help="Arguments to pass to dotnet (e.g., 'build -c Release')",
167
+ )
168
+ _git_parser = subparsers.add_parser("git", help="Execute git commands across all repositories")
169
+
170
+ # Upload command with arguments
171
+ upload_parser = subparsers.add_parser(
172
+ "upload", help="Upload app packages to the deployment server"
173
+ )
174
+ upload_parser.add_argument(
175
+ "package_path",
176
+ metavar="<path-to-app-package>",
177
+ help="Path to the app package file (.ipa, .msix, .aab, .apk)",
178
+ type=Path,
179
+ )
180
+ upload_parser.add_argument(
181
+ "--username",
182
+ required=True,
183
+ help="Name of the uploader",
184
+ metavar="<username>",
185
+ )
186
+ upload_parser.add_argument(
187
+ "--app-name",
188
+ required=True,
189
+ help="Name of the application",
190
+ metavar="<appname>",
191
+ )
192
+ upload_parser.add_argument(
193
+ "--platform",
194
+ required=True,
195
+ choices=["iOS", "Android", "Windows"],
196
+ help="Target platform",
197
+ metavar="<platform>",
198
+ )
199
+ upload_parser.add_argument(
200
+ "--release-type",
201
+ required=True,
202
+ choices=["Debug", "Release"],
203
+ help="Build configuration",
204
+ metavar="<type>",
205
+ )
206
+ upload_parser.add_argument(
207
+ "--version",
208
+ required=True,
209
+ help="Version number or identifier",
210
+ metavar="<version>",
211
+ )
212
+ upload_parser.add_argument(
213
+ "--endpoint",
214
+ required=True,
215
+ help="Base URL of the deployment server",
216
+ metavar="<url>",
217
+ )
218
+ upload_parser.add_argument(
219
+ "-n",
220
+ "--build-description",
221
+ help="Build notes/description",
222
+ metavar="<description>",
223
+ default=None,
224
+ )
225
+ upload_parser.add_argument(
226
+ "-N",
227
+ "--build-description-from-file",
228
+ help="Read build description from file",
229
+ metavar="<filepath>",
230
+ type=Path,
231
+ default=None,
232
+ )
233
+
234
+ demo_parser = subparsers.add_parser("demo", help="Run a built-in demo")
235
+ demo_parser.add_argument(
236
+ "name",
237
+ choices=list(_DEMOS),
238
+ metavar="name",
239
+ help=f"Demo to run. Choices: {', '.join(_DEMOS)}",
240
+ )
241
+
242
+ args: Namespace
243
+ unknownargs: list[str]
244
+ args, unknownargs = parser.parse_known_args()
245
+
246
+ if args.debug:
247
+ install(show_locals=True, suppress=[rich])
248
+
249
+ try:
250
+ return _run(console, args, unknownargs, parser)
251
+ except Exception as e:
252
+ if args.debug:
253
+ raise
254
+ console.print(f"[red]error:[/red] {e}")
255
+ return 1
256
+
257
+
258
+ def process_options(console: Console, config: Config, args: Namespace) -> bool:
259
+ """Process configuration options and return True if any were given.
260
+
261
+ Args:
262
+ console: Rich console for output
263
+ config: Loaded configuration
264
+ args: Parsed command line arguments
265
+
266
+ Returns:
267
+ True if any configuration option was given, False otherwise
268
+ """
269
+ config_path = args.config or files("pace.data").joinpath("pace.toml")
270
+
271
+ was_option_given = False
272
+ if args.print_config_path:
273
+ console.print(config_path)
274
+ was_option_given = True
275
+
276
+ if args.print_config:
277
+ console.print(config)
278
+ was_option_given = True
279
+
280
+ return was_option_given
281
+
282
+
283
+ def _run(
284
+ console: Console, args: Namespace, unknownargs: list[str], parser: argparse.ArgumentParser
285
+ ) -> int:
286
+ if args.config is not None:
287
+ if args.config.exists():
288
+ config = load_config(args.config, from_repo=args.from_repo, to_repo=args.to_repo)
289
+ else:
290
+ return 1
291
+ else:
292
+ config = load_config(from_repo=args.from_repo, to_repo=args.to_repo)
293
+
294
+ was_option_given = process_options(console, config, args)
295
+
296
+ match args.command:
297
+ case "clean":
298
+ clean.run(
299
+ console,
300
+ config,
301
+ cache=args.cache,
302
+ custom_cache=args.custom_cache,
303
+ project=args.project,
304
+ dry_run=args.dry_run,
305
+ )
306
+ case "dotnet":
307
+ return dotnet.run(console, config, args.dotnet_args)
308
+ case "git":
309
+ return git.run(console, config, unknownargs)
310
+ case "upload":
311
+ # Handle build description from file if provided
312
+ build_description = args.build_description
313
+ if args.build_description_from_file:
314
+ try:
315
+ build_description = args.build_description_from_file.read_text()
316
+ except Exception as e:
317
+ console.print(f"[red]Error reading build description file: {e}[/red]")
318
+ return 1
319
+ upload.run(
320
+ console,
321
+ args.package_path,
322
+ args.username,
323
+ args.app_name,
324
+ args.platform,
325
+ args.release_type,
326
+ args.version,
327
+ args.endpoint,
328
+ build_description,
329
+ )
330
+ case "demo":
331
+ _DEMOS[args.name](console, unknownargs)
332
+ case _:
333
+ if not was_option_given:
334
+ parser.print_help()
335
+ return 0
336
+
337
+
338
+ if __name__ == "__main__":
339
+ sys.exit(main())
@@ -0,0 +1,11 @@
1
+ """PACE command modules."""
2
+
3
+ from pace.commands import clean as clean_cmd
4
+ from pace.commands import dotnet as dotnet_cmd
5
+ from pace.commands import format as format_cmd
6
+ from pace.commands import git as git_cmd
7
+ from pace.commands import init as init_cmd
8
+ from pace.commands import test as test_cmd
9
+ from pace.commands import upload as upload_cmd
10
+
11
+ __all__ = ["clean_cmd", "dotnet_cmd", "format_cmd", "git_cmd", "init_cmd", "test_cmd", "upload_cmd"]