itw-python-builder 0.1.25__tar.gz → 0.1.26__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.25 → itw_python_builder-0.1.26}/PKG-INFO +1 -1
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder/tasks.py +84 -29
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/PKG-INFO +1 -1
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/pyproject.toml +1 -1
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/LICENSE +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/README.md +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder/.pylintrc +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder/__init__.py +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder/cli.py +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder/version.py +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/SOURCES.txt +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/dependency_links.txt +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/entry_points.txt +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/requires.txt +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/top_level.txt +0 -0
- {itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/setup.cfg +0 -0
|
@@ -88,7 +88,6 @@ def _parse_angular_env_file(path: str) -> dict:
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def load_env(ctx: Context = None):
|
|
91
|
-
"""Load env vars. Backend → .env in cwd. Frontend → src/environments/environment[.<branch>].ts."""
|
|
92
91
|
if detect_project_type() == 'frontend':
|
|
93
92
|
env_dir = os.path.join(os.getcwd(), 'src', 'environments')
|
|
94
93
|
candidates = []
|
|
@@ -96,11 +95,10 @@ def load_env(ctx: Context = None):
|
|
|
96
95
|
branch = get_current_branch(ctx)
|
|
97
96
|
candidates.append(os.path.join(env_dir, f'environment.{branch}.ts'))
|
|
98
97
|
candidates.append(os.path.join(env_dir, 'environment.ts'))
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
os.environ.setdefault(key, value)
|
|
98
|
+
for path in candidates:
|
|
99
|
+
if os.path.exists(path):
|
|
100
|
+
for key, value in _parse_angular_env_file(path).items():
|
|
101
|
+
os.environ.setdefault(key, value)
|
|
104
102
|
return
|
|
105
103
|
from decouple import Config, RepositoryEnv
|
|
106
104
|
env_path = os.path.join(os.getcwd(), '.env')
|
|
@@ -145,21 +143,15 @@ def get_latest_tag(ctx: Context) -> Version:
|
|
|
145
143
|
|
|
146
144
|
|
|
147
145
|
def _update_package_json_version(version: Version) -> None:
|
|
148
|
-
"""
|
|
146
|
+
"""Update the top-level `version` in package.json (preserves key order)."""
|
|
147
|
+
import json
|
|
149
148
|
pkg_path = os.path.join(os.getcwd(), 'package.json')
|
|
150
149
|
with open(pkg_path, 'r', encoding='utf-8') as fp:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
r'(^\s*"version"\s*:\s*")[^"]*(")',
|
|
154
|
-
lambda m: f'{m.group(1)}{version.bare_semver()}{m.group(2)}',
|
|
155
|
-
content,
|
|
156
|
-
count=1,
|
|
157
|
-
flags=re.MULTILINE,
|
|
158
|
-
)
|
|
159
|
-
if n == 0:
|
|
160
|
-
raise RuntimeError(f'Could not find "version" field in {pkg_path}')
|
|
150
|
+
data = json.load(fp)
|
|
151
|
+
data['version'] = version.bare_semver()
|
|
161
152
|
with open(pkg_path, 'w', encoding='utf-8') as fp:
|
|
162
|
-
|
|
153
|
+
json.dump(data, fp, indent=2)
|
|
154
|
+
fp.write('\n')
|
|
163
155
|
|
|
164
156
|
|
|
165
157
|
def read_version(ctx: Context) -> Version:
|
|
@@ -200,17 +192,76 @@ def get_gitlab_username(ctx: Context) -> str:
|
|
|
200
192
|
|
|
201
193
|
@task
|
|
202
194
|
def login(ctx: Context, username=None):
|
|
203
|
-
"""
|
|
195
|
+
"""Capture GitLab token. Backend also logs into the container registry."""
|
|
204
196
|
if not username:
|
|
205
197
|
username = get_gitlab_username(ctx)
|
|
206
|
-
|
|
207
|
-
token = getpass.getpass(f'Enter GitLab token (password) for {username}: ')
|
|
198
|
+
token = getpass.getpass(f'Enter GitLab token for {username}: ')
|
|
208
199
|
if not token:
|
|
209
200
|
raise RuntimeError('No token entered; aborting login.')
|
|
201
|
+
os.environ['GITLAB_TOKEN'] = token
|
|
202
|
+
os.environ['GITLAB_USERNAME'] = username
|
|
203
|
+
if detect_project_type() == 'backend':
|
|
204
|
+
print(f'Logging in to gitreg.it-works.io:443 as {username}')
|
|
205
|
+
ctx.run(
|
|
206
|
+
f'podman login -u {username} --password-stdin gitreg.it-works.io:443 --tls-verify=false',
|
|
207
|
+
in_stream=io.StringIO(token)
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
print(f'[itw] GitLab token captured for {username} (will be used for package upload).')
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _read_package_name() -> str:
|
|
214
|
+
"""Read 'name' field from package.json in cwd."""
|
|
215
|
+
import json
|
|
216
|
+
pkg_path = os.path.join(os.getcwd(), 'package.json')
|
|
217
|
+
with open(pkg_path, 'r', encoding='utf-8') as fp:
|
|
218
|
+
return json.load(fp)['name']
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _parse_gitlab_remote(ctx: Context) -> tuple:
|
|
222
|
+
"""Return (host, project_path) parsed from `git remote get-url origin`."""
|
|
223
|
+
result = ctx.run('git remote get-url origin', hide=True)
|
|
224
|
+
url = result.stdout.strip()
|
|
225
|
+
if url.endswith('.git'):
|
|
226
|
+
url = url[:-4]
|
|
227
|
+
if url.startswith('git@'):
|
|
228
|
+
host, path = url.split('@', 1)[1].split(':', 1)
|
|
229
|
+
elif '://' in url:
|
|
230
|
+
host, path = url.split('://', 1)[1].split('/', 1)
|
|
231
|
+
else:
|
|
232
|
+
raise RuntimeError(f'Cannot parse GitLab remote URL: {url}')
|
|
233
|
+
return host, path
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def build_frontend(ctx: Context, branch: str) -> None:
|
|
237
|
+
"""Build the Angular app. Master branch → production config; otherwise staging."""
|
|
238
|
+
config = 'production' if branch == 'master' else 'staging'
|
|
239
|
+
ctx.run(f'npx ng build --configuration={config}')
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def package_dist(ctx: Context) -> None:
|
|
243
|
+
"""Tar the dist/ output directory into dist.tar.gz."""
|
|
244
|
+
if not os.path.isdir(os.path.join(os.getcwd(), 'dist')):
|
|
245
|
+
raise RuntimeError('No dist/ directory found after build — did `ng build` succeed?')
|
|
246
|
+
ctx.run('tar -czf dist.tar.gz -C dist .')
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def upload_dist(ctx: Context, version) -> None:
|
|
250
|
+
"""Upload dist.tar.gz to this project's GitLab Generic Package Registry under <version>/."""
|
|
251
|
+
from urllib.parse import quote
|
|
252
|
+
if not os.environ.get('GITLAB_TOKEN'):
|
|
253
|
+
raise RuntimeError('GITLAB_TOKEN not set; run `itw login` first.')
|
|
254
|
+
host, path = _parse_gitlab_remote(ctx)
|
|
255
|
+
encoded = quote(path, safe='')
|
|
256
|
+
url = f'https://{host}/api/v4/projects/{encoded}/packages/generic/frontend/{version}/dist.tar.gz'
|
|
257
|
+
print(f'[itw] Uploading dist.tar.gz → {url}')
|
|
210
258
|
ctx.run(
|
|
211
|
-
|
|
212
|
-
|
|
259
|
+
'curl --fail --silent --show-error '
|
|
260
|
+
'--header "PRIVATE-TOKEN: $GITLAB_TOKEN" '
|
|
261
|
+
'--upload-file dist.tar.gz '
|
|
262
|
+
f'"{url}"'
|
|
213
263
|
)
|
|
264
|
+
print('[itw] Upload complete.')
|
|
214
265
|
|
|
215
266
|
|
|
216
267
|
def commit_version(ctx: Context, version: Version) -> None:
|
|
@@ -255,6 +306,11 @@ def tag_build_push(ctx: Context, version: Version, skip_pipeline: bool = False)
|
|
|
255
306
|
if project_type == 'backend':
|
|
256
307
|
buildimage(ctx)
|
|
257
308
|
pushimage(ctx)
|
|
309
|
+
else:
|
|
310
|
+
branch = get_current_branch(ctx)
|
|
311
|
+
build_frontend(ctx, branch)
|
|
312
|
+
package_dist(ctx)
|
|
313
|
+
upload_dist(ctx, version)
|
|
258
314
|
changelog(ctx, version)
|
|
259
315
|
push(ctx)
|
|
260
316
|
|
|
@@ -272,9 +328,8 @@ def taginit(ctx: Context) -> None:
|
|
|
272
328
|
def incrementrc(ctx: Context, skip_pipeline=False, pylintrc=None) -> None:
|
|
273
329
|
"""Increment release candidate version and deploy"""
|
|
274
330
|
check_branch(ctx)
|
|
331
|
+
login(ctx)
|
|
275
332
|
project_type = detect_project_type()
|
|
276
|
-
if project_type == 'backend':
|
|
277
|
-
login(ctx)
|
|
278
333
|
version = read_version(ctx)
|
|
279
334
|
version.increment_release_candidate()
|
|
280
335
|
if project_type == 'backend':
|
|
@@ -325,8 +380,7 @@ def incrementmajor(ctx: Context, skip_pipeline=False) -> None:
|
|
|
325
380
|
def release(ctx: Context, skip_pipeline=False) -> None:
|
|
326
381
|
"""Promote release candidate to stable release and deploy"""
|
|
327
382
|
_ = check_branch(ctx)
|
|
328
|
-
|
|
329
|
-
login(ctx)
|
|
383
|
+
login(ctx)
|
|
330
384
|
version = read_version(ctx)
|
|
331
385
|
version.reset_release_candidate(release=True)
|
|
332
386
|
tag_build_push(ctx, version, skip_pipeline)
|
|
@@ -407,9 +461,10 @@ def analyze(ctx: Context):
|
|
|
407
461
|
|
|
408
462
|
@task
|
|
409
463
|
def buildlocal(ctx: Context):
|
|
410
|
-
"""Build locally (Docker image for backend,
|
|
464
|
+
"""Build locally (Docker image for backend, ng build for frontend)"""
|
|
411
465
|
if detect_project_type() == 'frontend':
|
|
412
|
-
|
|
466
|
+
branch = get_current_branch(ctx)
|
|
467
|
+
build_frontend(ctx, branch)
|
|
413
468
|
print('✓ Built frontend (dist/)')
|
|
414
469
|
return
|
|
415
470
|
current_branch = get_current_branch(ctx)
|
|
@@ -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.26"
|
|
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
|
{itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/requires.txt
RENAMED
|
File without changes
|
{itw_python_builder-0.1.25 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|