pybuilder-integration 102__tar.gz → 104__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 (19) hide show
  1. {pybuilder_integration-102 → pybuilder_integration-104}/PKG-INFO +8 -2
  2. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/__init__.py +23 -1
  3. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/artifact_manager.py +1 -1
  4. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/properties.py +3 -0
  5. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/tasks.py +288 -12
  6. pybuilder_integration-104/pybuilder_integration/tool_utility.py +53 -0
  7. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/PKG-INFO +8 -2
  8. {pybuilder_integration-102 → pybuilder_integration-104}/setup.py +2 -1
  9. pybuilder_integration-102/pybuilder_integration/tool_utility.py +0 -24
  10. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/cloudwatchlogs_utility.py +0 -0
  11. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/directory_utility.py +0 -0
  12. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration/exec_utility.py +0 -0
  13. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/SOURCES.txt +0 -0
  14. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/dependency_links.txt +0 -0
  15. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/namespace_packages.txt +0 -0
  16. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/requires.txt +0 -0
  17. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/top_level.txt +0 -0
  18. {pybuilder_integration-102 → pybuilder_integration-104}/pybuilder_integration.egg-info/zip-safe +0 -0
  19. {pybuilder_integration-102 → pybuilder_integration-104}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pybuilder-integration
3
- Version: 102
3
+ Version: 104
4
4
  Summary: A pybuilder plugin that runs integration tests (Tavern & Cypress) against a target.
5
5
  Home-page: https://github.com/rspitler/pybuilder-integration
6
6
  Author:
@@ -13,5 +13,11 @@ Classifier: Programming Language :: Python
13
13
  Requires-Dist: pytest
14
14
  Requires-Dist: tavern==1.25.2
15
15
  Requires-Dist: boto3
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: home-page
19
+ Dynamic: license
20
+ Dynamic: requires-dist
21
+ Dynamic: summary
16
22
 
17
23
  A pybuilder plugin that runs integration tests against a target. This is intended to be a broader scope than unit-tests encompassing dependant functionality.
@@ -1,9 +1,10 @@
1
+ import json
1
2
  import os
2
3
 
3
4
  from pybuilder.core import task, Project, Logger, depends, after, init
4
5
  from pybuilder.reactor import Reactor
5
6
 
6
- import pybuilder_integration.tasks
7
+ import pybuilder_integration.tasks as tasks
7
8
  from pybuilder_integration.properties import *
8
9
 
9
10
 
@@ -18,6 +19,17 @@ def init_plugin(project):
18
19
  project.plugin_depends_on("pytest")
19
20
  project.build_depends_on('pytest-xdist')
20
21
  project.plugin_depends_on("tavern")
22
+ _apply_integration_properties_file(project)
23
+
24
+
25
+ def _apply_integration_properties_file(project):
26
+ properties_file = os.environ.get(SUBPROCESS_PROPERTIES_FILE_ENV)
27
+ if not properties_file or not os.path.exists(properties_file):
28
+ return
29
+ with open(properties_file) as fp:
30
+ properties = json.load(fp)
31
+ for key, value in properties.items():
32
+ project.set_property(key, value)
21
33
 
22
34
 
23
35
  @task(description="Runs integration tests against a CI/Prod environment."
@@ -34,6 +46,16 @@ def verify_environment(project: Project, logger: Logger, reactor: Reactor):
34
46
  tasks.verify_environment(project, logger, reactor)
35
47
 
36
48
 
49
+ @task(description="Run current build integration tests only. Intended for verify_environment subprocesses.")
50
+ def verify_environment_current(project: Project, logger: Logger, reactor: Reactor):
51
+ tasks._verify_environment_current(project, logger, reactor)
52
+
53
+
54
+ @task(description="Run downloaded LATEST integration tests only. Intended for verify_environment subprocesses.")
55
+ def verify_environment_latest(project: Project, logger: Logger, reactor: Reactor):
56
+ tasks._verify_environment_latest(project, logger, reactor)
57
+
58
+
37
59
  @task(description="Run integration tests using a cypress spec. Requires NPM installed.\n"
38
60
  f"\t{INTEGRATION_TARGET_URL} - (required) Full URL target for cypress tests\n"
39
61
  f"\t{INTEGRATION_PUBLIC_TARGET_URL} - (required) Full public URL target for cypress tests\n"
@@ -206,7 +206,7 @@ def _unzip_downloaded_artifacts(dir_with_zips: str, destination: str, logger: Lo
206
206
  shutil.copytree(f"{destination}/tavern/{dirn}", consolidated_folder, dirs_exist_ok=True)
207
207
  shutil.rmtree(f"{destination}/tavern/{dirn}")
208
208
  with open(f"{consolidated_folder}/roles", "a") as fp:
209
- fp.writelines(dirn)
209
+ fp.write(f"{dirn}\n")
210
210
  return destination
211
211
 
212
212
 
@@ -18,3 +18,6 @@ APPLICATION_GROUP = "application_group"
18
18
  RUN_PARALLEL = "pytest_parallel"
19
19
  CONSOLIDATE_TESTS = "consolidate_tavern"
20
20
  TESTING_SCOPE = "testing_scope"
21
+ PARALLEL_VERIFY_ENVIRONMENT = "parallel_verify_environment"
22
+ SUBPROCESS_PROPERTIES_FILE_ENV = "PYBUILDER_INTEGRATION_PROPERTIES_FILE"
23
+ CYPRESS_CACHE_FOLDER = "cypress_cache_folder"
@@ -1,5 +1,11 @@
1
+ import json
1
2
  import os
3
+ import re
2
4
  import shutil
5
+ import subprocess
6
+ import sys
7
+ import tempfile
8
+ import xml.etree.ElementTree as ET
3
9
 
4
10
  import pytest
5
11
  from pybuilder.core import Project, Logger, RequirementsFile
@@ -17,6 +23,29 @@ from pybuilder_integration.properties import *
17
23
  from pybuilder_integration.tool_utility import install_cypress
18
24
 
19
25
 
26
+ _VERIFY_ENVIRONMENT_SUBPROCESS_PROPERTIES = [
27
+ INTEGRATION_ARTIFACT_BUCKET,
28
+ ENVIRONMENT,
29
+ ARTIFACT_MANAGER,
30
+ CYPRESS_TEST_DIR,
31
+ CYPRESS_CONFIG_FILE,
32
+ INTEGRATION_TARGET_URL,
33
+ INTEGRATION_PUBLIC_TARGET_URL,
34
+ TAVERN_ADDITIONAL_ARGS,
35
+ TAVERN_TEST_DIR,
36
+ PROMOTE_ARTIFACT,
37
+ ROLE,
38
+ SHOULD_SKIP_LATEST,
39
+ APPLICATION,
40
+ APPLICATION_GROUP,
41
+ RUN_PARALLEL,
42
+ CONSOLIDATE_TESTS,
43
+ TESTING_SCOPE,
44
+ "abort_upload",
45
+ "record_cypress",
46
+ "verbose",
47
+ ]
48
+
20
49
  def integration_artifact_push(project: Project, logger: Logger, reactor: Reactor):
21
50
  logger.info("Starting upload of integration artifacts")
22
51
  manager = get_artifact_manager(project)
@@ -30,16 +59,153 @@ def integration_artifact_push(project: Project, logger: Logger, reactor: Reactor
30
59
 
31
60
 
32
61
  def verify_environment(project: Project, logger: Logger, reactor: Reactor):
33
- dist_directory = project.get_property(WORKING_TEST_DIR, get_working_distribution_directory(project))
62
+ if _get_bool_property(project, PARALLEL_VERIFY_ENVIRONMENT, False):
63
+ _verify_environment_parallel(project, logger, reactor)
64
+ return
65
+ _verify_environment_current(project, logger, reactor)
66
+ latest_directory = _download_latest_environment_artifacts(project, logger, reactor)
67
+ _verify_environment_latest(project, logger, reactor, latest_directory)
68
+ if _get_bool_property(project, PROMOTE_ARTIFACT, True):
69
+ integration_artifact_push(project=project, logger=logger, reactor=reactor)
70
+
71
+
72
+ def _verify_environment_current(project: Project, logger: Logger, reactor: Reactor, dist_directory=None):
73
+ if dist_directory is None:
74
+ dist_directory = project.get_property(WORKING_TEST_DIR, get_working_distribution_directory(project))
34
75
  logger.info(f"Preparing to run tests found in: {dist_directory}")
35
76
  _run_tests_in_directory(dist_directory, logger, project, reactor)
77
+
78
+
79
+ def _download_latest_environment_artifacts(project: Project, logger: Logger, reactor: Reactor):
36
80
  artifact_manager = get_artifact_manager(project=project)
37
- latest_directory = artifact_manager.download_artifacts(project=project, logger=logger, reactor=reactor)
81
+ return artifact_manager.download_artifacts(project=project, logger=logger, reactor=reactor)
82
+
83
+
84
+ def _verify_environment_latest(project: Project, logger: Logger, reactor: Reactor, latest_directory=None):
85
+ if latest_directory is None:
86
+ latest_directory = project.get_property(WORKING_TEST_DIR, None)
87
+ if latest_directory is None:
88
+ latest_directory = get_artifact_manager(project=project).download_artifacts(project=project,
89
+ logger=logger,
90
+ reactor=reactor)
38
91
  _run_tests_in_directory(latest_directory, logger, project, reactor, latest=True)
39
- if project.get_property(PROMOTE_ARTIFACT, True):
92
+
93
+
94
+ def _verify_environment_parallel(project: Project, logger: Logger, reactor: Reactor):
95
+ dist_directory = project.get_property(WORKING_TEST_DIR, get_working_distribution_directory(project))
96
+ logger.info(f"Preparing to run tests found in: {dist_directory}")
97
+ latest_directory = _download_latest_environment_artifacts(project, logger, reactor)
98
+ passes = [
99
+ {
100
+ "name": "current",
101
+ "task": "verify_environment_current",
102
+ "test_dir": dist_directory,
103
+ },
104
+ {
105
+ "name": "latest",
106
+ "task": "verify_environment_latest",
107
+ "test_dir": latest_directory,
108
+ },
109
+ ]
110
+ _run_verify_environment_passes_in_subprocesses(project, logger, passes)
111
+ if _get_bool_property(project, PROMOTE_ARTIFACT, True):
40
112
  integration_artifact_push(project=project, logger=logger, reactor=reactor)
41
113
 
42
114
 
115
+ def _run_verify_environment_passes_in_subprocesses(project: Project, logger: Logger, passes):
116
+ properties_file = _write_subprocess_properties_file(project)
117
+ processes = []
118
+ try:
119
+ for pass_config in passes:
120
+ process, log_file = _start_verify_environment_subprocess(project, logger, pass_config, properties_file)
121
+ processes.append((pass_config, process, log_file))
122
+
123
+ failures = []
124
+ for pass_config, process, log_file in processes:
125
+ exit_code = process.wait()
126
+ log_handle = getattr(process, "_pybuilder_integration_log_handle", None)
127
+ if log_handle:
128
+ log_handle.close()
129
+ if exit_code != 0:
130
+ failures.append(f"{pass_config['name']} exit_code={exit_code} log={log_file}")
131
+
132
+ if failures:
133
+ raise BuildFailedException("Parallel verify_environment failed: " + "; ".join(failures))
134
+ finally:
135
+ if os.path.exists(properties_file):
136
+ os.remove(properties_file)
137
+
138
+
139
+ def _start_verify_environment_subprocess(project: Project, logger: Logger, pass_config, properties_file):
140
+ log_file = os.path.join(prepare_logs_directory(project), f"verify_environment_{pass_config['name']}.log")
141
+ command = _build_verify_environment_subprocess_command(project, pass_config)
142
+ env = os.environ.copy()
143
+ env[SUBPROCESS_PROPERTIES_FILE_ENV] = properties_file
144
+ logger.info(f"Starting {pass_config['name']} verify_environment subprocess: {' '.join(command)}")
145
+ log_handle = os.fdopen(
146
+ os.open(log_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600),
147
+ "w",
148
+ encoding="utf-8",
149
+ )
150
+ try:
151
+ process = subprocess.Popen(command,
152
+ stdout=log_handle,
153
+ stderr=subprocess.STDOUT,
154
+ cwd=project.basedir,
155
+ env=env,
156
+ shell=False)
157
+ except Exception:
158
+ log_handle.close()
159
+ raise
160
+ process._pybuilder_integration_log_handle = log_handle
161
+ return process, log_file
162
+
163
+
164
+ def _build_verify_environment_subprocess_command(project: Project, pass_config):
165
+ stage_target = os.path.join(project.expand_path("$dir_target"), "verify_environment", pass_config["name"])
166
+ return [
167
+ sys.executable,
168
+ "-c",
169
+ "from pybuilder.cli import main; main()",
170
+ pass_config["task"],
171
+ "-P", f"{WORKING_TEST_DIR}={pass_config['test_dir']}",
172
+ "-P", f"{PARALLEL_VERIFY_ENVIRONMENT}=False",
173
+ "-P", f"{PROMOTE_ARTIFACT}=False",
174
+ "-P", f"dir_target={stage_target}",
175
+ "-P", f"dir_logs={os.path.join(stage_target, 'logs')}",
176
+ "-P", f"dir_reports={os.path.join(stage_target, 'reports')}",
177
+ ]
178
+
179
+
180
+ def _write_subprocess_properties_file(project: Project):
181
+ properties = _collect_json_safe_project_properties(project)
182
+ fd, path = tempfile.mkstemp(prefix="verify_environment_", suffix=".json", dir=prepare_logs_directory(project))
183
+ with os.fdopen(fd, "w") as fp:
184
+ json.dump(properties, fp)
185
+ return path
186
+
187
+
188
+ def _collect_json_safe_project_properties(project: Project):
189
+ safe_properties = {}
190
+ for name in _VERIFY_ENVIRONMENT_SUBPROCESS_PROPERTIES:
191
+ value = project.get_property(name, None)
192
+ if value is None:
193
+ continue
194
+ try:
195
+ json.dumps(value)
196
+ except (TypeError, ValueError):
197
+ continue
198
+ safe_properties[name] = value
199
+ return safe_properties
200
+
201
+
202
+ def _get_bool_property(project: Project, property_name, default=False):
203
+ value = project.get_property(property_name, default)
204
+ if isinstance(value, str):
205
+ return value.lower() in ["1", "true", "yes", "on"]
206
+ return bool(value)
207
+
208
+
43
209
  def _should_run_latest(test_dir, project):
44
210
  if project.get_property(SHOULD_SKIP_LATEST, False):
45
211
  if project.get_property(ROLE) == test_dir:
@@ -140,23 +306,108 @@ def _run_cypress_tests_in_directory(work_dir, logger, project, reactor: Reactor)
140
306
  f"screenshotsFolder={test_report_folder}/screenshots",
141
307
  "--reporter-options",
142
308
  f"mochaFile={results_file}"]
143
- if project.get_property("record_cypress", True):
309
+ if _get_bool_property(project, "record_cypress", True):
144
310
  args.append('--record')
145
311
  _add_config_file(logger, project, args, environment, work_dir)
146
312
  environment_variables = project.get_property(ENVIRONMENT_VARIABLES, {})
147
313
  logger.info(f"Running cypress on host: {target_url}")
148
- exec_utility.exec_command(command_name=executable, args=args,
149
- failure_message="Failed to execute cypress tests", log_file_name='cypress_run.log',
150
- project=project, reactor=reactor, logger=logger, working_dir=work_dir, report=False,
151
- env_vars=environment_variables)
152
- # workaround but cypress output are relative to location of cypress.json, so we need to collapse
153
- if os.path.exists(f"{work_dir}/target"):
154
- shutil.copytree(f"{work_dir}/target", "./target", dirs_exist_ok=True)
314
+ try:
315
+ exec_utility.exec_command(command_name=executable, args=args,
316
+ failure_message="Failed to execute cypress tests", log_file_name='cypress_run.log',
317
+ project=project, reactor=reactor, logger=logger, working_dir=work_dir, report=False,
318
+ env_vars=environment_variables)
319
+ # workaround but cypress output are relative to location of cypress.json, so we need to collapse
320
+ if os.path.exists(f"{work_dir}/target"):
321
+ shutil.copytree(f"{work_dir}/target", project.expand_path("$dir_target"), dirs_exist_ok=True)
322
+ finally:
323
+ _write_cypress_summary(project, logger)
155
324
  total_time.stop()
156
325
  logger.info(f"Ran Cypress Tests: {total_time.get_millis()}")
157
326
  return True
158
327
 
159
328
 
329
+ def _write_cypress_summary(project, logger):
330
+ """Parse cypress_run.log and write a portable Markdown summary artifact."""
331
+ log_path = os.path.join(prepare_logs_directory(project), "cypress_run.log")
332
+ summary_path = os.path.join(prepare_reports_directory(project), "cypress-summary.md")
333
+ try:
334
+ output = _build_cypress_summary(log_path)
335
+ with os.fdopen(
336
+ os.open(summary_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600),
337
+ "w",
338
+ encoding="utf-8",
339
+ ) as summary_file:
340
+ summary_file.write(output)
341
+ github_summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
342
+ if github_summary_path:
343
+ with os.fdopen(
344
+ os.open(github_summary_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600),
345
+ "a",
346
+ encoding="utf-8",
347
+ ) as f:
348
+ f.write(output)
349
+ print(output)
350
+ return summary_path
351
+ except Exception as e:
352
+ logger.warn(f"[cypress_summary] failed to generate summary: {e}")
353
+
354
+
355
+ def _build_cypress_summary(log_path):
356
+ if not os.path.exists(log_path):
357
+ return "## Cypress Test Results\n\n> warning `cypress_run.log` not found\n"
358
+
359
+ raw = open(log_path, errors="replace").read()
360
+ clean = re.sub(r"\x1b\[[0-9;]*m", "", raw)
361
+
362
+ total = failed = skipped = 0
363
+ failures = []
364
+ for xml_block in re.findall(r"<testsuites[\s\S]*?</testsuites>", clean):
365
+ try:
366
+ root = ET.fromstring(xml_block)
367
+ total += int(root.get("tests", 0))
368
+ failed += int(root.get("failures", 0))
369
+ skipped += int(root.get("skipped", 0) or 0)
370
+ for tc in root.iter("testcase"):
371
+ failure = tc.find("failure")
372
+ if failure is not None:
373
+ suite = tc.get("classname", "").split(".")[-1]
374
+ name = tc.get("name", "")
375
+ msg = (failure.get("message") or failure.text or "").split("\n")[0][:120]
376
+ failures.append((f"{suite} > {name}", msg))
377
+ except ET.ParseError:
378
+ pass
379
+
380
+ passed = total - failed - skipped
381
+ if total == 0:
382
+ icon = "WARN"
383
+ label = "No tests ran - check if Cypress found any spec files"
384
+ elif failed == 0:
385
+ icon = "PASS"
386
+ label = "All tests passed"
387
+ else:
388
+ icon = "FAIL"
389
+ label = f"{failed} test(s) failed"
390
+
391
+ lines = [
392
+ "## Cypress Test Results\n",
393
+ f"### {icon} {label}\n",
394
+ "| Total | Passed | Failed | Skipped |",
395
+ "|-------|--------|--------|---------|",
396
+ f"| {total} | {passed} | {failed} | {skipped} |",
397
+ ]
398
+
399
+ if failures:
400
+ lines.append("\n### Failed Tests\n")
401
+ for title, msg in failures[:20]:
402
+ lines.append(f"- **{title}**")
403
+ if msg:
404
+ lines.append(f" - _{msg}_")
405
+ if len(failures) > 20:
406
+ lines.append(f"\n_...and {len(failures) - 20} more_")
407
+
408
+ return "\n".join(lines) + "\n"
409
+
410
+
160
411
  def _add_config_file(logger, project, args, environment, work_dir):
161
412
 
162
413
  # Environment variable override
@@ -211,7 +462,7 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
211
462
  if project.get_property("verbose"):
212
463
  args.append("-s")
213
464
  args.append("-v")
214
- if project.get_property(RUN_PARALLEL, False):
465
+ if _get_bool_property(project, RUN_PARALLEL, False):
215
466
  args.extend(['-n', 'auto'])
216
467
  os.environ['TARGET'] = project.get_property(INTEGRATION_TARGET_URL)
217
468
  os.environ['PUBLIC_TARGET'] = project.get_property(INTEGRATION_PUBLIC_TARGET_URL)
@@ -223,6 +474,14 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
223
474
  os.chdir(test_dir)
224
475
  logger.debug(f"Running args: {args} ")
225
476
  ret = pytest.main(args)
477
+ if ret == pytest.ExitCode.NO_TESTS_COLLECTED:
478
+ fallback_args = _without_tavern_marker_filter(args)
479
+ if fallback_args != args:
480
+ logger.warn(
481
+ "No Tavern tests matched the marker filter; rerunning the full suite"
482
+ )
483
+ logger.debug(f"Running fallback args: {fallback_args} ")
484
+ ret = pytest.main(fallback_args)
226
485
  finally:
227
486
  os.chdir(cache_wd)
228
487
 
@@ -243,6 +502,23 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
243
502
  return True
244
503
 
245
504
 
505
+ def _without_tavern_marker_filter(args):
506
+ marker_options = {"-m", "--markexpr"}
507
+ fallback_args = []
508
+ skip_next = False
509
+ for arg in args:
510
+ if skip_next:
511
+ skip_next = False
512
+ continue
513
+ if arg in marker_options:
514
+ skip_next = True
515
+ continue
516
+ if arg.startswith("--markexpr="):
517
+ continue
518
+ fallback_args.append(arg)
519
+ return fallback_args
520
+
521
+
246
522
  def get_test_report_file(project, test_dir, tool="tavern"):
247
523
  run_name = os.path.basename(test_dir)
248
524
  output_file = os.path.join(prepare_reports_directory(project), f"{tool}-{run_name}.out.xml")
@@ -0,0 +1,53 @@
1
+ from pybuilder.core import Logger, Project
2
+ from pybuilder.reactor import Reactor
3
+
4
+ from pybuilder_integration.exec_utility import exec_command
5
+ from pybuilder_integration.properties import CYPRESS_CACHE_FOLDER
6
+
7
+
8
+ def install_cypress(logger: Logger, project: Project, reactor: Reactor, work_dir):
9
+ _verify_npm(reactor)
10
+ logger.info(f"Ensuring cypress is installed")
11
+ exec_command('npm', ['install', "cypress"], f'Failed to install cypress - required for integration tests',
12
+ f'{"cypress"}_npm_install.log', project, reactor, logger, report=False, working_dir=work_dir,
13
+ env_vars=_get_npm_env(project))
14
+
15
+
16
+ def _verify_npm(reactor):
17
+ reactor.pybuilder_venv.verify_can_execute(
18
+ command_and_arguments=["npm", "--version"], prerequisite="npm", caller="integration_tests")
19
+
20
+
21
+ def install_npm_dependencies(work_dir, project, logger, reactor):
22
+ _verify_npm(reactor)
23
+ install_command = 'ci' if _has_package_lock(work_dir) else 'install'
24
+ exec_command('npm', [install_command], f'Failed to install package.json - required for integration tests',
25
+ f'package_json_npm_install.log', project, reactor, logger, report=False, working_dir=work_dir,
26
+ env_vars=_get_npm_env(project))
27
+
28
+
29
+ def _has_package_lock(work_dir):
30
+ return any(
31
+ [
32
+ _path_exists(work_dir, "package-lock.json"),
33
+ _path_exists(work_dir, "npm-shrinkwrap.json"),
34
+ ]
35
+ )
36
+
37
+
38
+ def _path_exists(work_dir, filename):
39
+ import os
40
+ return os.path.exists(os.path.join(work_dir, filename))
41
+
42
+
43
+ def _get_npm_env(project):
44
+ import os
45
+ cache_folder = project.get_property(CYPRESS_CACHE_FOLDER, None)
46
+ if cache_folder:
47
+ return {"CYPRESS_CACHE_FOLDER": project.expand_path(cache_folder)}
48
+ cache_folder = os.environ.get("CYPRESS_CACHE_FOLDER")
49
+ if not cache_folder:
50
+ return {}
51
+ return {"CYPRESS_CACHE_FOLDER": cache_folder}
52
+
53
+
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pybuilder-integration
3
- Version: 102
3
+ Version: 104
4
4
  Summary: A pybuilder plugin that runs integration tests (Tavern & Cypress) against a target.
5
5
  Home-page: https://github.com/rspitler/pybuilder-integration
6
6
  Author:
@@ -13,5 +13,11 @@ Classifier: Programming Language :: Python
13
13
  Requires-Dist: pytest
14
14
  Requires-Dist: tavern==1.25.2
15
15
  Requires-Dist: boto3
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: home-page
19
+ Dynamic: license
20
+ Dynamic: requires-dist
21
+ Dynamic: summary
16
22
 
17
23
  A pybuilder plugin that runs integration tests against a target. This is intended to be a broader scope than unit-tests encompassing dependant functionality.
@@ -21,7 +21,7 @@ class install(_install):
21
21
  if __name__ == '__main__':
22
22
  setup(
23
23
  name = 'pybuilder-integration',
24
- version = '102',
24
+ version = '104',
25
25
  description = 'A pybuilder plugin that runs integration tests (Tavern & Cypress) against a target.',
26
26
  long_description = 'A pybuilder plugin that runs integration tests against a target. This is intended to be a broader scope than unit-tests encompassing dependant functionality.',
27
27
  long_description_content_type = None,
@@ -53,6 +53,7 @@ if __name__ == '__main__':
53
53
  'tavern==1.25.2',
54
54
  'boto3'
55
55
  ],
56
+ extras_require = {},
56
57
  dependency_links = [],
57
58
  zip_safe = True,
58
59
  cmdclass = {'install': install},
@@ -1,24 +0,0 @@
1
- from pybuilder.core import Logger, Project
2
- from pybuilder.reactor import Reactor
3
-
4
- from pybuilder_integration.exec_utility import exec_command
5
-
6
-
7
- def install_cypress(logger: Logger, project: Project, reactor: Reactor, work_dir):
8
- _verify_npm(reactor)
9
- logger.info(f"Ensuring cypress is installed")
10
- exec_command('npm', ['install', "cypress"], f'Failed to install cypress - required for integration tests',
11
- f'{"cypress"}_npm_install.log', project, reactor, logger, report=False, working_dir=work_dir)
12
-
13
-
14
- def _verify_npm(reactor):
15
- reactor.pybuilder_venv.verify_can_execute(
16
- command_and_arguments=["npm", "--version"], prerequisite="npm", caller="integration_tests")
17
-
18
-
19
- def install_npm_dependencies(work_dir, project, logger, reactor):
20
- _verify_npm(reactor)
21
- exec_command('npm', ['install'], f'Failed to install package.json - required for integration tests',
22
- f'package_json_npm_install.log', project, reactor, logger, report=False, working_dir=work_dir)
23
-
24
-