itw-python-builder 0.1.24__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.24 → itw_python_builder-0.1.26}/PKG-INFO +1 -1
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder/tasks.py +182 -42
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder/version.py +4 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/PKG-INFO +1 -1
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/pyproject.toml +1 -1
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/LICENSE +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/README.md +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder/.pylintrc +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder/__init__.py +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder/cli.py +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/SOURCES.txt +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/dependency_links.txt +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/entry_points.txt +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/requires.txt +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/top_level.txt +0 -0
- {itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/setup.cfg +0 -0
|
@@ -3,6 +3,7 @@ from itw_python_builder.version import Version
|
|
|
3
3
|
import getpass
|
|
4
4
|
import io
|
|
5
5
|
import os
|
|
6
|
+
import re
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
@@ -78,8 +79,27 @@ def detect_and_activate_venv():
|
|
|
78
79
|
print(f"[itw] Using venv: {venv_path}")
|
|
79
80
|
_venv_activated = True
|
|
80
81
|
|
|
81
|
-
def
|
|
82
|
-
"""
|
|
82
|
+
def _parse_angular_env_file(path: str) -> dict:
|
|
83
|
+
"""Extract string-valued keys from an Angular environment.ts object literal."""
|
|
84
|
+
with open(path, 'r', encoding='utf-8') as fp:
|
|
85
|
+
content = fp.read()
|
|
86
|
+
pattern = re.compile(r"""^\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*['"]([^'"]*)['"]""", re.MULTILINE)
|
|
87
|
+
return dict(pattern.findall(content))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def load_env(ctx: Context = None):
|
|
91
|
+
if detect_project_type() == 'frontend':
|
|
92
|
+
env_dir = os.path.join(os.getcwd(), 'src', 'environments')
|
|
93
|
+
candidates = []
|
|
94
|
+
if ctx is not None:
|
|
95
|
+
branch = get_current_branch(ctx)
|
|
96
|
+
candidates.append(os.path.join(env_dir, f'environment.{branch}.ts'))
|
|
97
|
+
candidates.append(os.path.join(env_dir, 'environment.ts'))
|
|
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)
|
|
102
|
+
return
|
|
83
103
|
from decouple import Config, RepositoryEnv
|
|
84
104
|
env_path = os.path.join(os.getcwd(), '.env')
|
|
85
105
|
if not os.path.exists(env_path):
|
|
@@ -122,6 +142,33 @@ def get_latest_tag(ctx: Context) -> Version:
|
|
|
122
142
|
return Version.parse(latest_tag)
|
|
123
143
|
|
|
124
144
|
|
|
145
|
+
def _update_package_json_version(version: Version) -> None:
|
|
146
|
+
"""Update the top-level `version` in package.json (preserves key order)."""
|
|
147
|
+
import json
|
|
148
|
+
pkg_path = os.path.join(os.getcwd(), 'package.json')
|
|
149
|
+
with open(pkg_path, 'r', encoding='utf-8') as fp:
|
|
150
|
+
data = json.load(fp)
|
|
151
|
+
data['version'] = version.bare_semver()
|
|
152
|
+
with open(pkg_path, 'w', encoding='utf-8') as fp:
|
|
153
|
+
json.dump(data, fp, indent=2)
|
|
154
|
+
fp.write('\n')
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def read_version(ctx: Context) -> Version:
|
|
158
|
+
"""Read current version. Backend → VERSION file. Frontend → latest git tag."""
|
|
159
|
+
if detect_project_type() == 'frontend':
|
|
160
|
+
return get_latest_tag(ctx)
|
|
161
|
+
return Version.read_from_file()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def save_version(version: Version) -> None:
|
|
165
|
+
"""Persist version. Backend → VERSION file. Frontend → package.json (bare semver)."""
|
|
166
|
+
if detect_project_type() == 'frontend':
|
|
167
|
+
_update_package_json_version(version)
|
|
168
|
+
return
|
|
169
|
+
version.save_to_file()
|
|
170
|
+
|
|
171
|
+
|
|
125
172
|
def generate_image_path(ctx: Context, branch: str) -> str:
|
|
126
173
|
result = ctx.run('git remote get-url origin')
|
|
127
174
|
result = result.stdout.splitlines()[0]
|
|
@@ -145,21 +192,83 @@ def get_gitlab_username(ctx: Context) -> str:
|
|
|
145
192
|
|
|
146
193
|
@task
|
|
147
194
|
def login(ctx: Context, username=None):
|
|
148
|
-
"""
|
|
195
|
+
"""Capture GitLab token. Backend also logs into the container registry."""
|
|
149
196
|
if not username:
|
|
150
197
|
username = get_gitlab_username(ctx)
|
|
151
|
-
|
|
152
|
-
token = getpass.getpass(f'Enter GitLab token (password) for {username}: ')
|
|
198
|
+
token = getpass.getpass(f'Enter GitLab token for {username}: ')
|
|
153
199
|
if not token:
|
|
154
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}')
|
|
155
258
|
ctx.run(
|
|
156
|
-
|
|
157
|
-
|
|
259
|
+
'curl --fail --silent --show-error '
|
|
260
|
+
'--header "PRIVATE-TOKEN: $GITLAB_TOKEN" '
|
|
261
|
+
'--upload-file dist.tar.gz '
|
|
262
|
+
f'"{url}"'
|
|
158
263
|
)
|
|
264
|
+
print('[itw] Upload complete.')
|
|
159
265
|
|
|
160
266
|
|
|
161
267
|
def commit_version(ctx: Context, version: Version) -> None:
|
|
162
|
-
|
|
268
|
+
if detect_project_type() == 'frontend':
|
|
269
|
+
ctx.run('git add package.json')
|
|
270
|
+
else:
|
|
271
|
+
ctx.run(f'git add {Version.VERSION_FILE_NAME}')
|
|
163
272
|
ctx.run('git add CHANGELOG.md')
|
|
164
273
|
ctx.run(f'git commit -m \'version update to {version}\'')
|
|
165
274
|
|
|
@@ -189,12 +298,19 @@ def pushimage(ctx: Context):
|
|
|
189
298
|
|
|
190
299
|
|
|
191
300
|
def tag_build_push(ctx: Context, version: Version, skip_pipeline: bool = False) -> None:
|
|
301
|
+
project_type = detect_project_type()
|
|
192
302
|
if not skip_pipeline:
|
|
193
303
|
test(ctx)
|
|
194
304
|
analyze(ctx)
|
|
195
305
|
tag(ctx, version)
|
|
196
|
-
|
|
197
|
-
|
|
306
|
+
if project_type == 'backend':
|
|
307
|
+
buildimage(ctx)
|
|
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)
|
|
198
314
|
changelog(ctx, version)
|
|
199
315
|
push(ctx)
|
|
200
316
|
|
|
@@ -205,7 +321,7 @@ def taginit(ctx: Context) -> None:
|
|
|
205
321
|
check_branch(ctx)
|
|
206
322
|
version = Version(0, 1, 0, 1)
|
|
207
323
|
tag(ctx, version)
|
|
208
|
-
version
|
|
324
|
+
save_version(version)
|
|
209
325
|
|
|
210
326
|
|
|
211
327
|
@task
|
|
@@ -213,11 +329,13 @@ def incrementrc(ctx: Context, skip_pipeline=False, pylintrc=None) -> None:
|
|
|
213
329
|
"""Increment release candidate version and deploy"""
|
|
214
330
|
check_branch(ctx)
|
|
215
331
|
login(ctx)
|
|
216
|
-
|
|
332
|
+
project_type = detect_project_type()
|
|
333
|
+
version = read_version(ctx)
|
|
217
334
|
version.increment_release_candidate()
|
|
218
|
-
|
|
335
|
+
if project_type == 'backend':
|
|
336
|
+
lint(ctx, pylintrc=pylintrc)
|
|
219
337
|
tag_build_push(ctx, version, skip_pipeline)
|
|
220
|
-
version
|
|
338
|
+
save_version(version)
|
|
221
339
|
commit_version(ctx, version)
|
|
222
340
|
push_version(ctx)
|
|
223
341
|
|
|
@@ -226,10 +344,10 @@ def incrementrc(ctx: Context, skip_pipeline=False, pylintrc=None) -> None:
|
|
|
226
344
|
def incrementpatch(ctx: Context, skip_pipeline=False) -> None:
|
|
227
345
|
"""Increment patch version, build, and deploy"""
|
|
228
346
|
production = check_branch(ctx)
|
|
229
|
-
version =
|
|
347
|
+
version = read_version(ctx)
|
|
230
348
|
version.increment_patch(release=production)
|
|
231
349
|
tag_build_push(ctx, version, skip_pipeline)
|
|
232
|
-
version
|
|
350
|
+
save_version(version)
|
|
233
351
|
commit_version(ctx, version)
|
|
234
352
|
push_version(ctx)
|
|
235
353
|
|
|
@@ -238,10 +356,10 @@ def incrementpatch(ctx: Context, skip_pipeline=False) -> None:
|
|
|
238
356
|
def incrementminor(ctx: Context, skip_pipeline=False) -> None:
|
|
239
357
|
"""Increment minor version, build, and deploy"""
|
|
240
358
|
production = check_branch(ctx)
|
|
241
|
-
version =
|
|
359
|
+
version = read_version(ctx)
|
|
242
360
|
version.increment_minor(release=production)
|
|
243
361
|
tag_build_push(ctx, version, skip_pipeline)
|
|
244
|
-
version
|
|
362
|
+
save_version(version)
|
|
245
363
|
commit_version(ctx, version)
|
|
246
364
|
push_version(ctx)
|
|
247
365
|
|
|
@@ -250,10 +368,10 @@ def incrementminor(ctx: Context, skip_pipeline=False) -> None:
|
|
|
250
368
|
def incrementmajor(ctx: Context, skip_pipeline=False) -> None:
|
|
251
369
|
"""Increment major version, build, and deploy"""
|
|
252
370
|
production = check_branch(ctx)
|
|
253
|
-
version =
|
|
371
|
+
version = read_version(ctx)
|
|
254
372
|
version.increment_major(release=production)
|
|
255
373
|
tag_build_push(ctx, version, skip_pipeline)
|
|
256
|
-
version
|
|
374
|
+
save_version(version)
|
|
257
375
|
commit_version(ctx, version)
|
|
258
376
|
push_version(ctx)
|
|
259
377
|
|
|
@@ -263,17 +381,20 @@ def release(ctx: Context, skip_pipeline=False) -> None:
|
|
|
263
381
|
"""Promote release candidate to stable release and deploy"""
|
|
264
382
|
_ = check_branch(ctx)
|
|
265
383
|
login(ctx)
|
|
266
|
-
version =
|
|
384
|
+
version = read_version(ctx)
|
|
267
385
|
version.reset_release_candidate(release=True)
|
|
268
386
|
tag_build_push(ctx, version, skip_pipeline)
|
|
269
|
-
version
|
|
387
|
+
save_version(version)
|
|
270
388
|
commit_version(ctx, version)
|
|
271
389
|
push_version(ctx)
|
|
272
390
|
|
|
273
391
|
|
|
274
392
|
@task
|
|
275
393
|
def test(ctx: Context, settings='backend.test_settings'):
|
|
276
|
-
"""Run
|
|
394
|
+
"""Run tests with coverage (Django or Angular/karma)"""
|
|
395
|
+
if detect_project_type() == 'frontend':
|
|
396
|
+
ctx.run('npx ng test --no-watch --code-coverage --browsers=ChromeHeadlessNoSandbox')
|
|
397
|
+
return
|
|
277
398
|
detect_and_activate_venv()
|
|
278
399
|
ctx.run(f'python -m coverage run manage.py test --settings={settings} --noinput -v 2')
|
|
279
400
|
ctx.run('python -m coverage report -m')
|
|
@@ -282,7 +403,10 @@ def test(ctx: Context, settings='backend.test_settings'):
|
|
|
282
403
|
|
|
283
404
|
@task
|
|
284
405
|
def lint(ctx: Context, pylintrc=None):
|
|
285
|
-
"""Run pylint, generate reports for SonarQube"""
|
|
406
|
+
"""Run pylint, generate reports for SonarQube (backend only)"""
|
|
407
|
+
if detect_project_type() == 'frontend':
|
|
408
|
+
print("[itw] Skipping lint for frontend (SonarQube handles code quality)")
|
|
409
|
+
return
|
|
286
410
|
detect_and_activate_venv()
|
|
287
411
|
rcfile = pylintrc if pylintrc else PYLINTRC
|
|
288
412
|
print("\n" + "=" * 60)
|
|
@@ -301,7 +425,10 @@ def lint(ctx: Context, pylintrc=None):
|
|
|
301
425
|
|
|
302
426
|
@task
|
|
303
427
|
def lintlocal(ctx: Context, pylintrc=None):
|
|
304
|
-
"""Run pylint locally with human-readable output (
|
|
428
|
+
"""Run pylint locally with human-readable output (backend only)"""
|
|
429
|
+
if detect_project_type() == 'frontend':
|
|
430
|
+
print("[itw] Skipping lint for frontend (SonarQube handles code quality)")
|
|
431
|
+
return
|
|
305
432
|
detect_and_activate_venv()
|
|
306
433
|
rcfile = pylintrc if pylintrc else PYLINTRC
|
|
307
434
|
print("\n" + "=" * 60)
|
|
@@ -313,10 +440,11 @@ def lintlocal(ctx: Context, pylintrc=None):
|
|
|
313
440
|
@task
|
|
314
441
|
def analyze(ctx: Context):
|
|
315
442
|
"""Run SonarQube analysis"""
|
|
316
|
-
|
|
317
|
-
|
|
443
|
+
if detect_project_type() == 'backend':
|
|
444
|
+
detect_and_activate_venv()
|
|
445
|
+
load_env(ctx)
|
|
318
446
|
try:
|
|
319
|
-
version =
|
|
447
|
+
version = read_version(ctx)
|
|
320
448
|
sonar_version = str(version).lstrip('v.')
|
|
321
449
|
except:
|
|
322
450
|
sonar_version = "0.0.0"
|
|
@@ -333,7 +461,12 @@ def analyze(ctx: Context):
|
|
|
333
461
|
|
|
334
462
|
@task
|
|
335
463
|
def buildlocal(ctx: Context):
|
|
336
|
-
"""Build Docker image
|
|
464
|
+
"""Build locally (Docker image for backend, ng build for frontend)"""
|
|
465
|
+
if detect_project_type() == 'frontend':
|
|
466
|
+
branch = get_current_branch(ctx)
|
|
467
|
+
build_frontend(ctx, branch)
|
|
468
|
+
print('✓ Built frontend (dist/)')
|
|
469
|
+
return
|
|
337
470
|
current_branch = get_current_branch(ctx)
|
|
338
471
|
local_tag = f'myapp:{current_branch}-local'
|
|
339
472
|
ctx.run(f'podman build . --tag={local_tag}')
|
|
@@ -342,7 +475,11 @@ def buildlocal(ctx: Context):
|
|
|
342
475
|
|
|
343
476
|
@task
|
|
344
477
|
def testlocal(ctx: Context, settings='backend.test_settings'):
|
|
345
|
-
"""Run tests locally (
|
|
478
|
+
"""Run tests locally (Django or Angular/karma)"""
|
|
479
|
+
if detect_project_type() == 'frontend':
|
|
480
|
+
ctx.run('npx ng test --no-watch --code-coverage --browsers=ChromeHeadlessNoSandbox')
|
|
481
|
+
print("✓ Tests completed")
|
|
482
|
+
return
|
|
346
483
|
detect_and_activate_venv()
|
|
347
484
|
ctx.run(f'python -m coverage run manage.py test --settings={settings} --noinput -v 2')
|
|
348
485
|
ctx.run('python -m coverage report -m')
|
|
@@ -353,11 +490,12 @@ def testlocal(ctx: Context, settings='backend.test_settings'):
|
|
|
353
490
|
@task
|
|
354
491
|
def analyzelocal(ctx: Context):
|
|
355
492
|
"""Run SonarQube static analysis locally"""
|
|
356
|
-
|
|
357
|
-
|
|
493
|
+
if detect_project_type() == 'backend':
|
|
494
|
+
detect_and_activate_venv()
|
|
495
|
+
load_env(ctx)
|
|
358
496
|
print("Running SonarQube analysis...")
|
|
359
497
|
try:
|
|
360
|
-
version =
|
|
498
|
+
version = read_version(ctx)
|
|
361
499
|
sonar_version = str(version).lstrip('v.')
|
|
362
500
|
except:
|
|
363
501
|
sonar_version = "0.0.0"
|
|
@@ -376,20 +514,22 @@ def analyzelocal(ctx: Context):
|
|
|
376
514
|
|
|
377
515
|
@task
|
|
378
516
|
def pipelinelocal(ctx: Context, settings='backend.test_settings', pylintrc=None):
|
|
379
|
-
"""Local pipeline: lint → test → analyze
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
517
|
+
"""Local pipeline: (lint →) test → analyze"""
|
|
518
|
+
project_type = detect_project_type()
|
|
519
|
+
if project_type == 'backend':
|
|
520
|
+
detect_and_activate_venv()
|
|
521
|
+
print("=" * 60)
|
|
522
|
+
print("Step 1: Running lint...")
|
|
523
|
+
print("=" * 60)
|
|
524
|
+
lint(ctx, pylintrc=pylintrc)
|
|
385
525
|
|
|
386
526
|
print("\n" + "=" * 60)
|
|
387
|
-
print("Step 2: Running tests...")
|
|
527
|
+
print(f"Step {'2' if project_type == 'backend' else '1'}: Running tests...")
|
|
388
528
|
print("=" * 60)
|
|
389
529
|
testlocal(ctx, settings)
|
|
390
530
|
|
|
391
531
|
print("\n" + "=" * 60)
|
|
392
|
-
print("Step 3: Running SonarQube analysis...")
|
|
532
|
+
print(f"Step {'3' if project_type == 'backend' else '2'}: Running SonarQube analysis...")
|
|
393
533
|
print("=" * 60)
|
|
394
534
|
analyzelocal(ctx)
|
|
395
535
|
|
|
@@ -467,7 +607,7 @@ def changelog(ctx: Context, version: Version = None):
|
|
|
467
607
|
version_str = str(version)
|
|
468
608
|
else:
|
|
469
609
|
try:
|
|
470
|
-
version_str = str(
|
|
610
|
+
version_str = str(read_version(ctx))
|
|
471
611
|
except:
|
|
472
612
|
version_str = "Unreleased"
|
|
473
613
|
|
|
@@ -76,6 +76,10 @@ class Version:
|
|
|
76
76
|
else:
|
|
77
77
|
raise ValueError(f'Invalid version: {version}')
|
|
78
78
|
|
|
79
|
+
def bare_semver(self) -> str:
|
|
80
|
+
"""Return just major.minor.patch (e.g. '2.0.4') for package.json."""
|
|
81
|
+
return f'{self.major}.{self.minor}.{self.patch}'
|
|
82
|
+
|
|
79
83
|
def __str__(self):
|
|
80
84
|
version = f'v.{self.major}.{self.minor}.{self.patch}'
|
|
81
85
|
if self.release_candidate == 0:
|
|
@@ -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
|
{itw_python_builder-0.1.24 → 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.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/requires.txt
RENAMED
|
File without changes
|
{itw_python_builder-0.1.24 → itw_python_builder-0.1.26}/itw_python_builder.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|