From 66b5d622bde9de2e1a93d8f82d8d1b3267400543 Mon Sep 17 00:00:00 2001 From: Elijah ben Izzy Date: Wed, 7 Jan 2026 20:53:02 -0800 Subject: [PATCH 1/6] Updates to clean up upload file names, upload everything --- scripts/apache_release.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/apache_release.py b/scripts/apache_release.py index 64965feb..4edba961 100644 --- a/scripts/apache_release.py +++ b/scripts/apache_release.py @@ -302,9 +302,9 @@ def _create_git_archive(version: str, rc_num: str, output_dir: str = "dist") -> os.makedirs(output_dir, exist_ok=True) - archive_name = f"apache-burr-{version}-incubating-src.tar.gz" + archive_name = f"apache-burr-{version}-incubating.tar.gz" archive_path = os.path.join(output_dir, archive_name) - prefix = f"apache-burr-{version}-incubating-src/" + prefix = f"apache-burr-{version}-incubating/" try: subprocess.run( @@ -383,7 +383,7 @@ def _build_sdist_from_git(version: str, output_dir: str = "dist") -> str: original_sdist = sdist_files[0] apache_sdist = os.path.join( - output_dir, f"apache-burr-{version.lower()}-incubating-src-sdist.tar.gz" + output_dir, f"apache-burr-{version.lower()}-incubating-sdist.tar.gz" ) if os.path.exists(apache_sdist): @@ -579,7 +579,9 @@ def _collect_all_artifacts(version: str, output_dir: str = "dist") -> list[str]: artifacts = [] for filename in os.listdir(output_dir): - if f"{version}-incubating" in filename: + # Match both incubating artifacts and wheel (which doesn't have -incubating suffix) + version_match = f"{version}-incubating" in filename or f"{version}" in filename + if version_match: if any(filename.endswith(ext) for ext in [".tar.gz", ".whl", ".asc", ".sha512"]): artifacts.append(os.path.join(output_dir, filename)) From baa78e3d7c9b0e7a80b09f9dec58eb017b07ec8e Mon Sep 17 00:00:00 2001 From: Elijah ben Izzy Date: Thu, 8 Jan 2026 15:08:02 -0800 Subject: [PATCH 2/6] WIP --- burr/cli/__main__.py | 10 ++++++---- pyproject.toml | 2 +- scripts/apache_release.py | 20 +++++++++----------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/burr/cli/__main__.py b/burr/cli/__main__.py index 109eb835..aeb56f7d 100644 --- a/burr/cli/__main__.py +++ b/burr/cli/__main__.py @@ -132,7 +132,8 @@ def cli(): pass -def _build_ui(): +def run_build_ui_bash_commands(): + """Execute the bash commands to build UI artifacts.""" cmd = "npm install --prefix telemetry/ui" _command(cmd, capture_output=False) cmd = "npm run build --prefix telemetry/ui" @@ -146,12 +147,13 @@ def _build_ui(): _command(cmd, capture_output=False) -@cli.command() +@cli.command(name="build-ui") def build_ui(): + """Build the UI artifacts from source.""" git_root = _get_git_root() logger.info("UI build: using project root %s", git_root) with cd(git_root): - _build_ui() + run_build_ui_bash_commands() BACKEND_MODULES = { @@ -246,7 +248,7 @@ def build_and_publish(prod: bool, no_wipe_dist: bool): git_root = _get_git_root() with cd(git_root): logger.info("Building UI -- this may take a bit...") - _build_ui() + build_ui() logger.info("Built UI!") if not no_wipe_dist: logger.info("Wiping dist/ directory for a clean publish.") diff --git a/pyproject.toml b/pyproject.toml index 7b8da0fa..e6e61e37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ build-backend = "flit_core.buildapi" [project] name = "apache-burr" -version = "0.40.2" +version = "0.41.0" dependencies = [] # yes, there are none requires-python = ">=3.9" authors = [ diff --git a/scripts/apache_release.py b/scripts/apache_release.py index 4edba961..27cdb921 100644 --- a/scripts/apache_release.py +++ b/scripts/apache_release.py @@ -401,7 +401,7 @@ def _build_sdist_from_git(version: str, output_dir: str = "dist") -> str: def _build_ui_artifacts() -> None: - """Build UI artifacts using burr-admin-build-ui.""" + """Build UI artifacts using burr's build function.""" print("Building UI artifacts...") ui_build_dir = "burr/tracking/server/build" @@ -410,18 +410,16 @@ def _build_ui_artifacts() -> None: if os.path.exists(ui_build_dir): shutil.rmtree(ui_build_dir) - # Check for burr-admin-build-ui - if shutil.which("burr-admin-build-ui") is None: - _fail("burr-admin-build-ui not found. Install with: pip install -e .[cli]") - - # Build UI - env = os.environ.copy() - env["BURR_PROJECT_ROOT"] = os.getcwd() - + # Import and call burr's build UI function directly + print(" Running burr's UI build...") try: - subprocess.run(["burr-admin-build-ui"], check=True, env=env, capture_output=True) + from burr.cli.__main__ import run_build_ui_bash_commands + + run_build_ui_bash_commands() print(" ✓ UI artifacts built successfully") - except subprocess.CalledProcessError as e: + except ImportError as e: + _fail(f"Could not import burr's build function. Make sure you're in the project root: {e}") + except Exception as e: _fail(f"Error building UI: {e}") # Verify From 94e32f7a7aecdc12943ff1e040d55bf58a104573 Mon Sep 17 00:00:00 2001 From: Elijah ben Izzy Date: Sat, 10 Jan 2026 11:22:19 -0800 Subject: [PATCH 3/6] WIP --- pyproject.toml | 2 +- scripts/apache_release.py | 159 +++++++++++++++++++++++++------------- 2 files changed, 106 insertions(+), 55 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e6e61e37..7b8da0fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ build-backend = "flit_core.buildapi" [project] name = "apache-burr" -version = "0.41.0" +version = "0.40.2" dependencies = [] # yes, there are none requires-python = ">=3.9" authors = [ diff --git a/scripts/apache_release.py b/scripts/apache_release.py index 27cdb921..7a728005 100644 --- a/scripts/apache_release.py +++ b/scripts/apache_release.py @@ -79,6 +79,46 @@ def _print_step(step_num: int, total: int, description: str) -> None: print("-" * 80) +def _run_command( + cmd: list[str], + description: str, + error_message: str, + success_message: Optional[str] = None, + capture_output: bool = True, + **kwargs, +) -> subprocess.CompletedProcess: + """Run a subprocess command with consistent error handling and output. + + Args: + cmd: Command and arguments as list + description: What we're doing (printed before running) + error_message: Error message prefix if command fails + success_message: Optional success message (printed after if provided) + capture_output: Whether to capture stdout/stderr (default True) + **kwargs: Additional arguments to pass to subprocess.run + + Returns: + CompletedProcess instance + """ + if description: + print(f" {description}") + + try: + result = subprocess.run( + cmd, + check=True, + capture_output=capture_output, + text=True, + **kwargs, + ) + if success_message: + print(f" ✓ {success_message}") + return result + except subprocess.CalledProcessError as e: + error_detail = f": {e.stderr}" if capture_output and e.stderr else "" + _fail(f"{error_message}{error_detail}") + + # ============================================================================ # Environment Validation # ============================================================================ @@ -200,14 +240,13 @@ def _sign_artifact(artifact_path: str) -> tuple[str, str]: checksum_path = f"{artifact_path}.sha512" # GPG signature - try: - subprocess.run( - ["gpg", "--armor", "--output", signature_path, "--detach-sig", artifact_path], - check=True, - ) - print(f" ✓ Created GPG signature: {signature_path}") - except subprocess.CalledProcessError as e: - _fail(f"Error signing artifact: {e}") + _run_command( + ["gpg", "--armor", "--output", signature_path, "--detach-sig", artifact_path], + description="", + error_message="Error signing artifact", + capture_output=False, + ) + print(f" ✓ Created GPG signature: {signature_path}") # SHA512 checksum sha512_hash = hashlib.sha512() @@ -306,22 +345,21 @@ def _create_git_archive(version: str, rc_num: str, output_dir: str = "dist") -> archive_path = os.path.join(output_dir, archive_name) prefix = f"apache-burr-{version}-incubating/" - try: - subprocess.run( - [ - "git", - "archive", - "HEAD", - f"--prefix={prefix}", - "--format=tar.gz", - "--output", - archive_path, - ], - check=True, - ) - print(f" ✓ Created git archive: {archive_path}") - except subprocess.CalledProcessError as e: - _fail(f"Error creating git archive: {e}") + _run_command( + [ + "git", + "archive", + "HEAD", + f"--prefix={prefix}", + "--format=tar.gz", + "--output", + archive_path, + ], + description="", + error_message="Error creating git archive", + capture_output=False, + ) + print(f" ✓ Created git archive: {archive_path}") file_size = os.path.getsize(archive_path) print(f" ✓ Archive size: {file_size:,} bytes") @@ -359,20 +397,15 @@ def _build_sdist_from_git(version: str, output_dir: str = "dist") -> str: _remove_ui_build_artifacts() _check_git_working_tree() - print(" Running flit build --format sdist...") - try: - env = os.environ.copy() - env["FLIT_USE_VCS"] = "0" - subprocess.run( - ["flit", "build", "--format", "sdist"], - env=env, - capture_output=True, - text=True, - check=True, - ) - print(" ✓ flit sdist created successfully") - except subprocess.CalledProcessError as e: - _fail(f"Failed to build sdist: {e.stderr}") + env = os.environ.copy() + env["FLIT_USE_VCS"] = "0" + _run_command( + ["flit", "build", "--format", "sdist"], + description="Running flit build --format sdist...", + error_message="Failed to build sdist", + success_message="flit sdist created successfully", + env=env, + ) # Find and rename sdist expected_pattern = f"dist/apache_burr-{version.lower()}.tar.gz" @@ -401,26 +434,46 @@ def _build_sdist_from_git(version: str, output_dir: str = "dist") -> str: def _build_ui_artifacts() -> None: - """Build UI artifacts using burr's build function.""" + """Build UI artifacts (npm build + copy to burr/tracking/server/build). + + This replicates the logic from burr.cli.__main__.run_build_ui_bash_commands() + without requiring burr to be installed. + """ print("Building UI artifacts...") + ui_source_dir = "telemetry/ui" ui_build_dir = "burr/tracking/server/build" # Clean existing UI build if os.path.exists(ui_build_dir): shutil.rmtree(ui_build_dir) - # Import and call burr's build UI function directly - print(" Running burr's UI build...") - try: - from burr.cli.__main__ import run_build_ui_bash_commands + # Install npm dependencies + _run_command( + ["npm", "install", "--prefix", ui_source_dir], + description="Installing npm dependencies...", + error_message="npm install failed", + success_message="npm dependencies installed", + ) + + # Build UI with npm + _run_command( + ["npm", "run", "build", "--prefix", ui_source_dir], + description="Building UI with npm...", + error_message="npm build failed", + success_message="npm build completed", + ) + + # Copy build artifacts + print(" Copying build artifacts...") + os.makedirs(ui_build_dir, exist_ok=True) + ui_output = os.path.join(ui_source_dir, "build") - run_build_ui_bash_commands() - print(" ✓ UI artifacts built successfully") - except ImportError as e: - _fail(f"Could not import burr's build function. Make sure you're in the project root: {e}") + try: + shutil.copytree(ui_output, ui_build_dir, dirs_exist_ok=True) + print(" ✓ Build artifacts copied") except Exception as e: - _fail(f"Error building UI: {e}") + _fail(f"Failed to copy build artifacts: {e}") # Verify if not os.path.exists(ui_build_dir) or not os.listdir(ui_build_dir): @@ -503,13 +556,13 @@ def _build_wheel_from_current_dir(version: str, output_dir: str = "dist") -> str env = os.environ.copy() env["FLIT_USE_VCS"] = "0" - subprocess.run( + _run_command( ["flit", "build", "--format", "wheel"], + description="", + error_message="Wheel build failed", + success_message="Wheel built successfully", env=env, - check=True, - capture_output=True, ) - print(" ✓ Wheel built successfully") # Find the wheel wheel_pattern = f"dist/apache_burr-{version}*.whl" @@ -523,8 +576,6 @@ def _build_wheel_from_current_dir(version: str, output_dir: str = "dist") -> str return wheel_path - except subprocess.CalledProcessError as e: - _fail(f"Wheel build failed: {e}") finally: # Always restore symlinks if copied: From 035007d0636f563b0999821dee546774350e166b Mon Sep 17 00:00:00 2001 From: Elijah ben Izzy Date: Sat, 10 Jan 2026 11:55:51 -0800 Subject: [PATCH 4/6] Adds instructions on voter flow --- scripts/README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/scripts/README.md b/scripts/README.md index 92393c9b..e444b445 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -99,6 +99,39 @@ python scripts/apache_release.py all 0.41.0 0 your_apache_id --no-upload Output: `dist/` directory with tar.gz (archive + sdist), whl, plus .asc and .sha512 files. Install from the whl file to test it out after runnig the `wheel` subcommand. +## For Voters: Verifying a Release + +If you're voting on a release, follow these steps to verify the release candidate: + +### Quick Start + +```bash +# 1. Download and extract the source archive +wget https://dist.apache.org/repos/dist/dev/incubator/burr/{VERSION}-incubating-RC{N}/apache-burr-{VERSION}-incubating.tar.gz +tar -xzf apache-burr-{VERSION}-incubating.tar.gz +cd apache-burr-{VERSION}-incubating/ + +# 2. Verify signatures and checksums +python scripts/verify_apache_artifacts.py signatures + +# 3. Install build dependencies +pip install flit + +# 4. Build the wheel from source +python scripts/apache_release.py wheel {VERSION} {RC_NUM} + +# 5. Install and test the wheel +pip install dist/apache_burr-{VERSION}-py3-none-any.whl +python -c "import burr; print('Burr version:', burr.__version__)" +burr --version # Test CLI + +# 6. (Optional) Run tests +pip install -e ".[tests]" +pytest tests/ +``` + +See the "Verification" section below for more detailed verification steps. + ## Verification Validate artifacts before uploading or voting. Checks GPG signatures, SHA512 checksums, archive integrity, and license compliance with Apache RAT. The `list-contents` command is useful for inspecting what's actually packaged in each artifact. From 2f7830e2120d4a5788cb6d333e0052c583f869dc Mon Sep 17 00:00:00 2001 From: Elijah ben Izzy Date: Sat, 10 Jan 2026 11:58:40 -0800 Subject: [PATCH 5/6] Adds forgotten license --- docs/_templates/page.html | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/_templates/page.html b/docs/_templates/page.html index 8ac03996..b7dfa780 100644 --- a/docs/_templates/page.html +++ b/docs/_templates/page.html @@ -1,3 +1,22 @@ + + {% extends "!page.html" %} {% block extrahead %} From 504947b85c932cac24d69b5cc73b82b5ea423c46 Mon Sep 17 00:00:00 2001 From: Elijah ben Izzy Date: Sat, 10 Jan 2026 11:58:52 -0800 Subject: [PATCH 6/6] Adds voter flow instructions to README/script --- scripts/README.md | 13 +++++-------- scripts/apache_release.py | 12 ++++-------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index e444b445..6d96a1a7 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -120,16 +120,13 @@ pip install flit # 4. Build the wheel from source python scripts/apache_release.py wheel {VERSION} {RC_NUM} -# 5. Install and test the wheel -pip install dist/apache_burr-{VERSION}-py3-none-any.whl -python -c "import burr; print('Burr version:', burr.__version__)" -burr --version # Test CLI - -# 6. (Optional) Run tests -pip install -e ".[tests]" -pytest tests/ +# 5. Install and run the wheel +pip install "dist/apache_burr-{VERSION}-py3-none-any.whl[learn]" +burr ``` +You can run tests, etc... from here + See the "Verification" section below for more detailed verification steps. ## Verification diff --git a/scripts/apache_release.py b/scripts/apache_release.py index 7a728005..d4c28afc 100644 --- a/scripts/apache_release.py +++ b/scripts/apache_release.py @@ -711,18 +711,14 @@ def _generate_vote_email(version: str, rc_num: str, svn_url: str) -> str: The Git tag to be voted upon is: {tag} -Release artifacts are signed with your GPG key. The KEYS file is available at: +Release artifacts are signed with the release manager's GPG key. The KEYS file is available at: https://downloads.apache.org/incubator/{PROJECT_SHORT_NAME}/KEYS Please download, verify, and test the release candidate. -Some ideas to verify the release: -1. Build from source - see README in scripts/ directory for instructions -2. Install the wheel using pip to test functionality -3. Run license verification using the verify_apache_artifacts.py script or manually check - - Verify checksums and signatures match - - Check LICENSE/NOTICE files are present - - Ensure all source files have Apache headers +For detailed step-by-step instructions on how to verify this release, please see the +"For Voters: Verifying a Release" section in the scripts/README.md file within the +source archive. The vote will run for a minimum of 72 hours. Please vote: