File indexing completed on 2026-03-28 07:48:52
0001 import os
0002 import shutil
0003 import subprocess
0004 import sys
0005 import argparse
0006 import json
0007 import re
0008
0009
0010 script_path = os.path.dirname(os.path.abspath(__file__))
0011
0012
0013 firebird_ng_path = os.path.abspath(os.path.join(script_path, 'firebird-ng'))
0014 dist_path = os.path.join(firebird_ng_path, 'dist', 'firebird', 'browser')
0015 static_path = os.path.join(script_path, 'pyrobird', 'pyrobird', 'server', 'static')
0016 doc_path = os.path.join(script_path, 'doc')
0017 dist_doc_path = os.path.join(dist_path, 'assets', 'doc')
0018 package_json_path = os.path.join(firebird_ng_path, 'package.json')
0019 pyrobird_version_path = os.path.join(script_path, 'pyrobird', 'pyrobird', '__version__.py')
0020 pyrobird_path = os.path.join(script_path, 'pyrobird')
0021
0022
0023 print(f"Script Path: {script_path}")
0024 print(f"Docs: {doc_path}")
0025 print(f"Firebird NG: {firebird_ng_path}")
0026 print(f"NG dist: {dist_path}")
0027 print(f"NG dist doc: {dist_doc_path}")
0028 print(f"Flask static Path: {static_path}")
0029
0030
0031 def _run(command, cwd, prefix):
0032 """Run a subprocess command with output prefixing. Raises on failure."""
0033 proc = subprocess.Popen(
0034 command,
0035 cwd=cwd,
0036 text=True,
0037 stdout=subprocess.PIPE,
0038 stderr=subprocess.STDOUT,
0039 )
0040
0041 for line in proc.stdout:
0042 print(f"[{prefix}] " + line, end="")
0043
0044 proc.wait()
0045 if proc.returncode:
0046 raise subprocess.CalledProcessError(proc.returncode, command)
0047
0048
0049 def update_npm_version(version, is_dry_run):
0050 """Update version in firebird-ng/package.json"""
0051 print(f"Updating {package_json_path} to version {version}")
0052 if not is_dry_run:
0053 with open(package_json_path, 'r') as f:
0054 package_data = json.load(f)
0055 package_data['version'] = version
0056 with open(package_json_path, 'w') as f:
0057 json.dump(package_data, f, indent=2)
0058 f.write('\n')
0059
0060
0061 def update_py_version(version, is_dry_run):
0062 """Update version in pyrobird/__version__.py"""
0063 print(f"Updating {pyrobird_version_path} to version {version}")
0064 if not is_dry_run:
0065 with open(pyrobird_version_path, 'r') as f:
0066 content = f.read()
0067 content = re.sub(
0068 r'__version__\s*=\s*["\'][^"\']*["\']',
0069 f'__version__ = "{version}"',
0070 content
0071 )
0072 with open(pyrobird_version_path, 'w') as f:
0073 f.write(content)
0074
0075
0076 def build_ng(is_dry_run):
0077 """Build Angular frontend"""
0078 print("Running build at firebird-ng")
0079 if is_dry_run:
0080 return
0081
0082 _run(["npm", "run", "build"], cwd=firebird_ng_path, prefix="ng")
0083
0084
0085 def test_frontend(is_dry_run):
0086 """Run headless tests for the Angular frontend"""
0087 print("Running headless tests for firebird-ng")
0088 if is_dry_run:
0089 return
0090
0091 _run(["npm", "run", "test:headless"], cwd=firebird_ng_path, prefix="ng-test")
0092 print("Frontend tests passed!")
0093
0094
0095 def test_backend(is_dry_run):
0096 """Run pytest tests for pyrobird backend"""
0097 print("Running pytest tests for pyrobird")
0098 if is_dry_run:
0099 return
0100
0101 print(f"Using Python: {sys.executable}")
0102 _run([sys.executable, "-m", "pytest", "./tests/unit_tests", "-v"], cwd=pyrobird_path, prefix="pytest")
0103 print("Backend tests passed!")
0104
0105
0106 def test_all(is_dry_run):
0107 """Run all tests (frontend and backend)"""
0108 test_frontend(is_dry_run)
0109 test_backend(is_dry_run)
0110
0111
0112 def copy_frontend(is_dry_run):
0113
0114
0115 if os.path.exists(static_path):
0116 print(f"Removing existing '{static_path}'")
0117
0118 if not is_dry_run:
0119 shutil.rmtree(static_path)
0120
0121 print(f"mkdir '{static_path}'")
0122 if not is_dry_run:
0123 os.makedirs(static_path)
0124
0125
0126 print(f"Copying '{dist_path}' to '{static_path}' ")
0127 if is_dry_run:
0128 return
0129
0130 if os.path.exists(dist_path):
0131 shutil.copytree(dist_path, static_path, dirs_exist_ok=True)
0132 else:
0133 print(f"Source directory {dist_path} does not exist.")
0134 sys.exit(1)
0135
0136
0137 def copy_docs(is_dry_run):
0138
0139
0140 print(f"Copying '{doc_path}' to '{dist_doc_path}' ")
0141
0142 if not os.path.exists(dist_doc_path):
0143 print(f"Source directory {doc_path} does not exist.")
0144 sys.exit(1)
0145
0146 if not is_dry_run:
0147 shutil.copytree(doc_path, dist_doc_path, dirs_exist_ok=True)
0148
0149
0150 def build_py(is_dry_run):
0151 """Build pyrobird package using uv"""
0152 print("Building pyrobird package with uv")
0153 if is_dry_run:
0154 return
0155
0156 _run(["uv", "build"], cwd=pyrobird_path, prefix="uv-build")
0157 print("Python build completed!")
0158
0159
0160 def publish_py(is_dry_run):
0161 """Print the command to publish pyrobird package"""
0162 print("To publish pyrobird package, run:")
0163 print(f" cd {pyrobird_path} && uv publish")
0164
0165
0166 def main():
0167 """Main is main! la-la la-la-la"""
0168
0169 parser = argparse.ArgumentParser(description="Helper script that builds everything and places in the right places")
0170 parser.add_argument("mode", nargs="*", default="", help="all, py, test Or itemized: build_ng, cp_ng, test_frontend, test_backend, py_build, py_publish")
0171 parser.add_argument("-d","--dry-run", action="store_true", help="Don't do actual files operations")
0172 parser.add_argument("-v", "--version", help="Set version for both frontend and pyrobird packages")
0173 args = parser.parse_args()
0174
0175
0176 if args.version:
0177 update_npm_version(args.version, is_dry_run=args.dry_run)
0178 update_py_version(args.version, is_dry_run=args.dry_run)
0179
0180
0181 mode = args.mode[0] if args.mode else ""
0182 if mode in ["all", "ng", "build_ng", "build-ng"]:
0183 build_ng(is_dry_run=args.dry_run)
0184
0185 if mode in ["all", "test"]:
0186 test_all(is_dry_run=args.dry_run)
0187
0188 if mode in ["test_frontend", "ng", "test-frontend"]:
0189 test_frontend(is_dry_run=args.dry_run)
0190
0191 if mode in ["test_backend", "test-backend"]:
0192 test_backend(is_dry_run=args.dry_run)
0193
0194 if mode in ["all", "cp_ng"]:
0195 copy_frontend(is_dry_run=args.dry_run)
0196
0197 if mode in ["all", "py", "py_build", "py-build"]:
0198 build_py(is_dry_run=args.dry_run)
0199
0200 if mode in ["all", "py", "py_publish", "py-publish"]:
0201 publish_py(is_dry_run=args.dry_run)
0202
0203 if __name__ == "__main__":
0204 main()