itw-python-builder 0.1.37__tar.gz → 0.1.39__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.
Files changed (20) hide show
  1. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/PKG-INFO +1 -1
  2. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/tasks.py +20 -24
  3. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/utils.py +39 -0
  4. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder.egg-info/PKG-INFO +1 -1
  5. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/pyproject.toml +1 -1
  6. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/LICENSE +0 -0
  7. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/README.md +0 -0
  8. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/.pylintrc +0 -0
  9. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/__init__.py +0 -0
  10. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/cli.py +0 -0
  11. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/ssr_tasks.py +0 -0
  12. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/templates/server.sitemap.snippet.ts +0 -0
  13. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/templates/sitemap.routes.ts +0 -0
  14. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder/version.py +0 -0
  15. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder.egg-info/SOURCES.txt +0 -0
  16. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder.egg-info/dependency_links.txt +0 -0
  17. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder.egg-info/entry_points.txt +0 -0
  18. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder.egg-info/requires.txt +0 -0
  19. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/itw_python_builder.egg-info/top_level.txt +0 -0
  20. {itw_python_builder-0.1.37 → itw_python_builder-0.1.39}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: itw_python_builder
3
- Version: 0.1.37
3
+ Version: 0.1.39
4
4
  Summary: Standardized Django deployment pipeline with Docker, testing, and SonarQube integration
5
5
  Author-email: IT-Works <contact@it-works.io>
6
6
  License: MIT
@@ -12,6 +12,9 @@ from itw_python_builder.utils import (
12
12
  get_gitlab_username,
13
13
  _parse_gitlab_remote,
14
14
  is_ssr_project,
15
+ install_sigint_guard,
16
+ abort_if_interrupted,
17
+ step,
15
18
  )
16
19
  from itw_python_builder.ssr_tasks import * # noqa: F401,F403 — exposes ssr-init
17
20
 
@@ -61,18 +64,9 @@ def build_frontend(ctx: Context, branch: str, ssr: bool = False) -> None:
61
64
 
62
65
  def package_dist(ctx: Context) -> None:
63
66
  """Tar the dist/ output directory into dist.tar.gz."""
64
- dist_dir = os.path.join(os.getcwd(), 'dist')
65
- if not os.path.isdir(dist_dir):
67
+ if not os.path.isdir(os.path.join(os.getcwd(), 'dist')):
66
68
  raise RuntimeError('No dist/ directory found after build — did `ng build` succeed?')
67
-
68
- target_dir = dist_dir
69
- if not is_ssr_project():
70
- for root, dirs, files in os.walk(dist_dir):
71
- if 'index.html' in files:
72
- target_dir = root
73
- break
74
-
75
- ctx.run(f'tar -czf dist.tar.gz -C "{target_dir}" .')
69
+ ctx.run('tar -czf dist.tar.gz -C dist .')
76
70
 
77
71
 
78
72
  _GITLAB_API_HOST_MAP = {
@@ -133,21 +127,22 @@ def pushimage(ctx: Context):
133
127
 
134
128
 
135
129
  def tag_build_push(ctx: Context, version: Version, skip_pipeline: bool = False, ssr: bool = False) -> None:
130
+ install_sigint_guard()
136
131
  project_type = detect_project_type()
137
132
  if not skip_pipeline:
138
- test(ctx)
139
- analyze(ctx)
140
- tag(ctx, version)
133
+ step('tests', test, ctx)
134
+ step('analyze', analyze, ctx)
135
+ step('tag', tag, ctx, version)
141
136
  if project_type == 'backend':
142
- buildimage(ctx)
143
- pushimage(ctx)
137
+ step('build image', buildimage, ctx)
138
+ step('push image', pushimage, ctx)
144
139
  else:
145
140
  branch = get_current_branch(ctx)
146
- build_frontend(ctx, branch, ssr=ssr)
147
- package_dist(ctx)
148
- upload_dist(ctx, version)
149
- changelog(ctx, version)
150
- push(ctx)
141
+ step('build', build_frontend, ctx, branch, ssr=ssr)
142
+ step('package', package_dist, ctx)
143
+ step('upload', upload_dist, ctx, version)
144
+ step('changelog', changelog, ctx, version)
145
+ step('push', push, ctx)
151
146
 
152
147
 
153
148
  @task(name='tag-init')
@@ -364,23 +359,24 @@ def analyzelocal(ctx: Context):
364
359
  @task
365
360
  def pipelinelocal(ctx: Context, settings='backend.test_settings', pylintrc=None):
366
361
  """Local pipeline: (lint →) test → analyze"""
362
+ install_sigint_guard()
367
363
  project_type = detect_project_type()
368
364
  if project_type == 'backend':
369
365
  detect_and_activate_venv()
370
366
  print("=" * 60)
371
367
  print("Step 1: Running lint...")
372
368
  print("=" * 60)
373
- lint(ctx, pylintrc=pylintrc)
369
+ step('lint', lint, ctx, pylintrc=pylintrc)
374
370
 
375
371
  print("\n" + "=" * 60)
376
372
  print(f"Step {'2' if project_type == 'backend' else '1'}: Running tests...")
377
373
  print("=" * 60)
378
- testlocal(ctx, settings)
374
+ step('tests', testlocal, ctx, settings)
379
375
 
380
376
  print("\n" + "=" * 60)
381
377
  print(f"Step {'3' if project_type == 'backend' else '2'}: Running SonarQube analysis...")
382
378
  print("=" * 60)
383
- analyzelocal(ctx)
379
+ step('analyze', analyzelocal, ctx)
384
380
 
385
381
  print("\n" + "=" * 60)
386
382
  print("✓ Local pipeline completed successfully!")
@@ -7,6 +7,8 @@ import io
7
7
  import json
8
8
  import os
9
9
  import re
10
+ import signal
11
+ import sys
10
12
  from pathlib import Path
11
13
 
12
14
  from invoke import Context
@@ -15,6 +17,43 @@ from itw_python_builder.version import Version
15
17
 
16
18
  _venv_activated = False
17
19
 
20
+ _INTERRUPTED = False
21
+ _SIGINT_INSTALLED = False
22
+
23
+
24
+ def _sigint_handler(signum, frame):
25
+ global _INTERRUPTED
26
+ _INTERRUPTED = True
27
+ raise KeyboardInterrupt
28
+
29
+
30
+ def install_sigint_guard() -> None:
31
+ """Install a process-wide SIGINT trap."""
32
+ global _SIGINT_INSTALLED
33
+ if _SIGINT_INSTALLED:
34
+ return
35
+ signal.signal(signal.SIGINT, _sigint_handler)
36
+ _SIGINT_INSTALLED = True
37
+
38
+
39
+ def abort_if_interrupted(step: str = '') -> None:
40
+ """Exit 130 if a SIGINT was received since the last call."""
41
+ if _INTERRUPTED:
42
+ where = f' during {step}' if step else ''
43
+ print(f'\n[itw] Aborted by user (Ctrl+C){where}.')
44
+ sys.exit(130)
45
+
46
+
47
+ def step(label: str, fn, *args, **kwargs):
48
+ """Run `fn(*args, **kwargs)`, swallow KeyboardInterrupt, then bail if SIGINT was seen."""
49
+ result = None
50
+ try:
51
+ result = fn(*args, **kwargs)
52
+ except KeyboardInterrupt:
53
+ pass
54
+ abort_if_interrupted(label)
55
+ return result
56
+
18
57
 
19
58
  def detect_project_type() -> str:
20
59
  """Detect whether the current directory is a 'frontend' or 'backend' project."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: itw_python_builder
3
- Version: 0.1.37
3
+ Version: 0.1.39
4
4
  Summary: Standardized Django deployment pipeline with Docker, testing, and SonarQube integration
5
5
  Author-email: IT-Works <contact@it-works.io>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "itw_python_builder"
7
- version = "0.1.37"
7
+ version = "0.1.39"
8
8
  description = "Standardized Django deployment pipeline with Docker, testing, and SonarQube integration"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"