itw-python-builder 0.1.31__tar.gz → 0.1.33__tar.gz
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.
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/PKG-INFO +1 -1
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/ssr_tasks.py +169 -25
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/PKG-INFO +1 -1
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/pyproject.toml +1 -1
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/LICENSE +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/README.md +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/.pylintrc +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/__init__.py +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/cli.py +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/tasks.py +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/templates/server.sitemap.snippet.ts +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/templates/sitemap.routes.ts +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/utils.py +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder/version.py +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/SOURCES.txt +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/dependency_links.txt +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/entry_points.txt +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/requires.txt +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/top_level.txt +0 -0
- {itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/setup.cfg +0 -0
|
@@ -43,6 +43,147 @@ def _angular_core_installed_version() -> str:
|
|
|
43
43
|
return json.load(fp).get('version', '')
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
def _read_angular_json() -> dict:
|
|
47
|
+
"""Read angular.json from cwd. Returns empty dict if missing/invalid."""
|
|
48
|
+
path = os.path.join(os.getcwd(), 'angular.json')
|
|
49
|
+
if not os.path.isfile(path):
|
|
50
|
+
return {}
|
|
51
|
+
try:
|
|
52
|
+
with open(path, 'r', encoding='utf-8') as fp:
|
|
53
|
+
return json.load(fp)
|
|
54
|
+
except (OSError, json.JSONDecodeError):
|
|
55
|
+
return {}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _detect_angular_project(angular_json: dict) -> tuple:
|
|
59
|
+
"""Return (project_name, project_data) for the project SSR was scaffolded into.
|
|
60
|
+
|
|
61
|
+
Priority: package.json `name` match → defaultProject → only project in repo.
|
|
62
|
+
Returns (None, None) if angular.json has no usable projects.
|
|
63
|
+
"""
|
|
64
|
+
projects = angular_json.get('projects') or {}
|
|
65
|
+
if not projects:
|
|
66
|
+
return (None, None)
|
|
67
|
+
try:
|
|
68
|
+
pkg_name = _read_package_name()
|
|
69
|
+
if pkg_name in projects:
|
|
70
|
+
return (pkg_name, projects[pkg_name])
|
|
71
|
+
except Exception:
|
|
72
|
+
pass
|
|
73
|
+
default = angular_json.get('defaultProject')
|
|
74
|
+
if default and default in projects:
|
|
75
|
+
return (default, projects[default])
|
|
76
|
+
if len(projects) == 1:
|
|
77
|
+
name = next(iter(projects))
|
|
78
|
+
return (name, projects[name])
|
|
79
|
+
return (None, None)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _maybe_add_ssr_scripts(ctx: Context) -> None:
|
|
83
|
+
"""Add build:ssr, build:ssr:staging, build:ssr:dev npm scripts based on angular.json.
|
|
84
|
+
|
|
85
|
+
Reads angular.json to detect:
|
|
86
|
+
- The builder (legacy `:browser` + separate `server` target, or modern `:application`)
|
|
87
|
+
- The project name (for `ng run <name>:server:<config>` in the legacy form)
|
|
88
|
+
- Which configurations exist (only writes scripts for configs that exist in both
|
|
89
|
+
`build` and, for legacy, `server` targets)
|
|
90
|
+
|
|
91
|
+
Never overwrites a script that already exists in package.json. Prints a summary
|
|
92
|
+
of what was added vs preserved vs skipped (no matching config).
|
|
93
|
+
"""
|
|
94
|
+
angular_json = _read_angular_json()
|
|
95
|
+
if not angular_json:
|
|
96
|
+
print('[itw] No angular.json found — skipping SSR script setup.')
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
project_name, project = _detect_angular_project(angular_json)
|
|
100
|
+
if not project:
|
|
101
|
+
print('[itw] Could not detect Angular project in angular.json — skipping SSR script setup.')
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
arch = project.get('architect') or project.get('targets') or {}
|
|
105
|
+
build = arch.get('build') or {}
|
|
106
|
+
server = arch.get('server') or {}
|
|
107
|
+
builder = build.get('builder', '')
|
|
108
|
+
|
|
109
|
+
is_legacy = builder == '@angular-devkit/build-angular:browser' and bool(server)
|
|
110
|
+
is_modern = builder == '@angular-devkit/build-angular:application'
|
|
111
|
+
if not (is_legacy or is_modern):
|
|
112
|
+
print(f'[itw] Unrecognized build builder {builder!r} — skipping SSR script setup.')
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
build_configs = set((build.get('configurations') or {}).keys())
|
|
116
|
+
if is_legacy:
|
|
117
|
+
server_configs = set((server.get('configurations') or {}).keys())
|
|
118
|
+
usable_configs = build_configs & server_configs
|
|
119
|
+
builder_label = 'legacy (browser + server targets)'
|
|
120
|
+
else:
|
|
121
|
+
usable_configs = build_configs
|
|
122
|
+
builder_label = 'modern (application builder)'
|
|
123
|
+
|
|
124
|
+
print(f"[itw] Detected {builder_label} for project '{project_name}'.")
|
|
125
|
+
|
|
126
|
+
def cmd_for(config: str) -> str:
|
|
127
|
+
if is_legacy:
|
|
128
|
+
return f'ng build --configuration={config} && ng run {project_name}:server:{config}'
|
|
129
|
+
return f'ng build --configuration={config}'
|
|
130
|
+
|
|
131
|
+
script_map = {
|
|
132
|
+
'production': 'build:ssr',
|
|
133
|
+
'staging': 'build:ssr:staging',
|
|
134
|
+
'development': 'build:ssr:dev',
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
pkg_path = os.path.join(os.getcwd(), 'package.json')
|
|
138
|
+
with open(pkg_path, 'r', encoding='utf-8') as fp:
|
|
139
|
+
data = json.load(fp)
|
|
140
|
+
scripts = data.setdefault('scripts', {})
|
|
141
|
+
|
|
142
|
+
# The "ng add" schematic writes `build:ssr` as the implicit form
|
|
143
|
+
# `ng build && ng run <name>:server` (relies on Angular defaults). It works
|
|
144
|
+
# but it's not explicit. We normalize JUST that exact stub to the explicit
|
|
145
|
+
# form; any other existing value (custom prebuild steps, etc.) is preserved.
|
|
146
|
+
ng_add_stub = f'ng build && ng run {project_name}:server'
|
|
147
|
+
|
|
148
|
+
added, normalized, preserved, skipped = [], [], [], []
|
|
149
|
+
for config_name, script_name in script_map.items():
|
|
150
|
+
if config_name not in usable_configs:
|
|
151
|
+
skipped.append((script_name, config_name))
|
|
152
|
+
continue
|
|
153
|
+
existing = scripts.get(script_name)
|
|
154
|
+
is_stub = (
|
|
155
|
+
script_name == 'build:ssr'
|
|
156
|
+
and existing is not None
|
|
157
|
+
and ' '.join(existing.split()) == ng_add_stub
|
|
158
|
+
)
|
|
159
|
+
if existing is not None and not is_stub:
|
|
160
|
+
preserved.append(script_name)
|
|
161
|
+
continue
|
|
162
|
+
new_cmd = cmd_for(config_name)
|
|
163
|
+
scripts[script_name] = new_cmd
|
|
164
|
+
if is_stub:
|
|
165
|
+
normalized.append((script_name, new_cmd))
|
|
166
|
+
else:
|
|
167
|
+
added.append((script_name, new_cmd))
|
|
168
|
+
|
|
169
|
+
if added or normalized:
|
|
170
|
+
with open(pkg_path, 'w', encoding='utf-8') as fp:
|
|
171
|
+
json.dump(data, fp, indent=2)
|
|
172
|
+
fp.write('\n')
|
|
173
|
+
|
|
174
|
+
print('[itw] SSR npm scripts:')
|
|
175
|
+
for name, cmd in added:
|
|
176
|
+
print(f' + added: {name} → {cmd}')
|
|
177
|
+
for name, cmd in normalized:
|
|
178
|
+
print(f' ~ normalized: {name} → {cmd} (was ng-add\'s implicit form)')
|
|
179
|
+
for name in preserved:
|
|
180
|
+
print(f' = preserved: {name} (already in package.json, not overwritten)')
|
|
181
|
+
for name, cfg in skipped:
|
|
182
|
+
print(f" - skipped: {name} (no '{cfg}' config in angular.json)")
|
|
183
|
+
if not (added or normalized or preserved):
|
|
184
|
+
print(' (nothing added)')
|
|
185
|
+
|
|
186
|
+
|
|
46
187
|
def _pin_ssr_deps_in_package_json(target_version: str) -> list:
|
|
47
188
|
"""Pin @angular/ssr and @angular/platform-server in package.json to exact target_version.
|
|
48
189
|
|
|
@@ -118,6 +259,24 @@ def _run_ng_add_ssr(ctx: Context) -> None:
|
|
|
118
259
|
print(f'[itw] Running `ng add {package_spec}` (Angular {major}, this may take a minute)...')
|
|
119
260
|
ctx.run(f'npx ng add {package_spec} {flags}')
|
|
120
261
|
|
|
262
|
+
# `ng add` typically rewrites package.json with a caret prefix (e.g. "^18.2.14"),
|
|
263
|
+
# and its internal `npm install` then resolves that range to the LATEST patch
|
|
264
|
+
# (often newer than the installed @angular/core), reintroducing the strict-peer
|
|
265
|
+
# conflict and pulling in @angular/ssr versions that reference symbols not in
|
|
266
|
+
# the installed @angular/platform-browser. Force-pin again post-ng-add and
|
|
267
|
+
# reinstall with --save-exact to lock the exact versions across package.json,
|
|
268
|
+
# package-lock.json, and node_modules.
|
|
269
|
+
if installed_core:
|
|
270
|
+
re_pinned = _pin_ssr_deps_in_package_json(installed_core)
|
|
271
|
+
if re_pinned:
|
|
272
|
+
print(f'[itw] Re-pinned {", ".join(re_pinned)} → {installed_core} (ng add introduced a caret range).')
|
|
273
|
+
print(f'[itw] Locking installed versions to {installed_core} with --save-exact...')
|
|
274
|
+
ctx.run(
|
|
275
|
+
f'npm install --save-exact '
|
|
276
|
+
f'@angular/ssr@{installed_core} '
|
|
277
|
+
f'@angular/platform-server@{installed_core}'
|
|
278
|
+
)
|
|
279
|
+
|
|
121
280
|
|
|
122
281
|
def _install_ngx_seo_helper(ctx: Context) -> None:
|
|
123
282
|
print('[itw] Installing ngx-seo-helper...')
|
|
@@ -182,29 +341,12 @@ def _print_next_steps(project_name: str) -> None:
|
|
|
182
341
|
print()
|
|
183
342
|
print(' 1. Add `NgxSeoModule` to your AppModule imports.')
|
|
184
343
|
print(' 2. Fill in src/sitemap.routes.ts with your route tree.')
|
|
185
|
-
print(' 3.
|
|
186
|
-
print(' angular.json
|
|
187
|
-
print('
|
|
188
|
-
print()
|
|
189
|
-
print('
|
|
190
|
-
print('
|
|
191
|
-
print()
|
|
192
|
-
print(' "build:ssr": "<build command for production>"')
|
|
193
|
-
print(' "build:ssr:staging": "<build command for staging>"')
|
|
194
|
-
print()
|
|
195
|
-
print(' The exact command depends on which builder angular.json uses:')
|
|
196
|
-
print()
|
|
197
|
-
print(' • Modern application builder (default for fresh ng add @angular/ssr):')
|
|
198
|
-
print(' "build:ssr": "ng build --configuration=production"')
|
|
199
|
-
print(' "build:ssr:staging": "ng build --configuration=staging"')
|
|
200
|
-
print()
|
|
201
|
-
print(' • Legacy browser builder (older projects with a separate server target):')
|
|
202
|
-
print(f' "build:ssr": "ng build --configuration=production && ng run {project_name}:server:production"')
|
|
203
|
-
print(f' "build:ssr:staging": "ng build --configuration=staging && ng run {project_name}:server:staging"')
|
|
204
|
-
print()
|
|
205
|
-
print(' 5. Verify locally: npm run build:ssr && npm run serve:ssr')
|
|
206
|
-
print(' 6. Commit the new files.')
|
|
207
|
-
print(' 7. Deploy with: itw incrementrc --ssr (or incrementpatch/release --ssr)')
|
|
344
|
+
print(' 3. If `build:ssr:staging` was skipped above, add a `staging` configuration')
|
|
345
|
+
print(f' to angular.json (and the `server` target, if legacy builder), then re-run')
|
|
346
|
+
print(' `itw ssr-init --force` to add the missing scripts.')
|
|
347
|
+
print(' 4. Verify locally: npm run build:ssr && npm run serve:ssr')
|
|
348
|
+
print(' 5. Commit the new files.')
|
|
349
|
+
print(' 6. Deploy with: itw incrementrc --ssr (or incrementpatch/release --ssr)')
|
|
208
350
|
print('=' * 70)
|
|
209
351
|
|
|
210
352
|
|
|
@@ -212,8 +354,9 @@ def _print_next_steps(project_name: str) -> None:
|
|
|
212
354
|
def ssr_init(ctx: Context, force: bool = False) -> None:
|
|
213
355
|
"""One-time SSR scaffold: ng add @angular/ssr, ngx-seo-helper, sitemap stub, server.ts patch.
|
|
214
356
|
|
|
215
|
-
|
|
216
|
-
|
|
357
|
+
Auto-adds the build:ssr / build:ssr:staging / build:ssr:dev npm scripts based
|
|
358
|
+
on angular.json (detects builder type and available configs). Existing scripts
|
|
359
|
+
are never overwritten.
|
|
217
360
|
"""
|
|
218
361
|
if detect_project_type() != 'frontend':
|
|
219
362
|
raise RuntimeError('ssr-init only runs on frontend (Angular) projects.')
|
|
@@ -230,6 +373,7 @@ def ssr_init(ctx: Context, force: bool = False) -> None:
|
|
|
230
373
|
if not is_ssr_project():
|
|
231
374
|
_run_ng_add_ssr(ctx)
|
|
232
375
|
|
|
376
|
+
_maybe_add_ssr_scripts(ctx)
|
|
233
377
|
_install_ngx_seo_helper(ctx)
|
|
234
378
|
_write_sitemap_template()
|
|
235
379
|
_patch_server_ts()
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "itw_python_builder"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.33"
|
|
8
8
|
description = "Standardized Django deployment pipeline with Docker, testing, and SonarQube integration"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/requires.txt
RENAMED
|
File without changes
|
{itw_python_builder-0.1.31 → itw_python_builder-0.1.33}/itw_python_builder.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|