| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| --> |
| |
| # Policy on source versus distribution |
| |
| Apache Hamilton is an apache-incubating project. As such, we intend to follow all Apache guidelines to |
| both the spirit and (when applicable) the letter. |
| |
| That said, there is occasional ambiguity. Thus we aim to clarify with a reasonable and consistently maintained |
| approach. The question that we found most ambiguous when determining our release process is: |
| 1. What counts as source code, and should thus be included in the "sdist" (the source-only distribution) |
| 2. What should be included in the build? |
| |
| Specifically, we set the following guidelines: |
| |
| | | source (to vote on) -- tar.gz | sdist -- source used to build | whl file | Reasoning | |
| |---|---|---|---|---| |
| | Build Scripts | Y | Y | N | Included in tar.gz and sdist as they are needed to reproduce the build, but not in the whl. These are only meant to be consumed by developers/pod members. | |
| | Library Source code | Y | Y | Y | Core library source code is included in all three distributions: tar.gz, sdist, and whl. | |
| | Tests (unit + plugin) | Y | Y | N | We expect users/PMC to download the source distribution, build from source, run the tests, and validate. Thus we include in the tar.gz and sdist, but not in the whl. | |
| | READMEs | Y | Y | Y | Standard project metadata files (README.md, LICENSE, NOTICE, DISCLAIMER) are included in all three distributions. | |
| | Documentation | Y | N | N | Documentation source is included in the tar.gz for voters to review, but not in the sdist or whl as it is not needed for building or using the package. | |
| | Representative Examples | Y | Y | N | A curated set of examples are included in tar.gz and sdist so voters can verify Hamilton works end-to-end. Not in the whl as they serve as documentation/verification only. | |
| | Other Examples | Y | N | N | These are included in the tar.gz for voters to review but not included in the sdist or whl. | |
| |
| # Packages |
| |
| Apache Hamilton consists of 5 independently versioned packages: |
| |
| | Package | Key | Working Directory | Description | |
| |---|---|---|---| |
| | `apache-hamilton` | `hamilton` | `.` | Core library (must be released first) | |
| | `apache-hamilton-sdk` | `sdk` | `ui/sdk` | Tracking SDK | |
| | `apache-hamilton-contrib` | `contrib` | `contrib` | Community dataflows | |
| | `apache-hamilton-ui` | `ui` | `ui/backend` | Web UI server | |
| | `apache-hamilton-lsp` | `lsp` | `dev_tools/language_server` | Language server | |
| |
| The core `apache-hamilton` package must be released first. The other four packages depend on it but not on each other. |
| |
| # Scripts Reference |
| |
| | Script | Purpose | |
| |---|---| |
| | `scripts/apache_release_helper.py` | Build artifacts, sign, tag, upload RC to SVN, generate vote email | |
| | `scripts/promote_rc.sh` | Move a voted RC from SVN dev to SVN release | |
| | `scripts/tag_release.sh` | Create the final (non-RC) `-incubating` release tag on the merge commit | |
| | `scripts/verify_apache_artifacts.py` | Verify GPG signatures, checksums, and license headers | |
| | `scripts/verification-script.sh` | End-to-end RC validation (download, verify, build, test, examples) | |
| | `scripts/generate_announce_email.py` | Generate vote result, announcement email, and Slack message | |
| | `scripts/qualify.sh` | Run a sampling of examples for quick qualification | |
| | `scripts/setup_keys.sh` | Set up GPG keys for signing | |
| | `sf-hamilton-redirect/build.sh` | Build the `sf-hamilton` redirect package for a given version | |
| |
| # Release Process |
| |
| ## Environment Setup |
| |
| We recommend using [uv](https://docs.astral.sh/uv/) for Python environment management. It handles Python versions, virtual environments, and dependency installation in a single tool. |
| |
| ### Prerequisites |
| |
| - Python 3.10+ |
| - `uv` ([install guide](https://docs.astral.sh/uv/getting-started/installation/)) |
| - `flit` for building |
| - `twine` for package validation |
| - GPG key configured for signing |
| - Node.js + npm for UI builds (only needed for the `ui` package) |
| - Apache RAT jar for license checking (optional, for verification) |
| |
| ```bash |
| # Install uv (unless already installed) |
| curl -LsSf https://astral.sh/uv/install.sh | sh |
| |
| # Create a virtual environment with build dependencies |
| uv venv --python 3.11 |
| uv sync --group release |
| |
| # Verify GPG setup |
| gpg --list-secret-keys |
| |
| # IMPORTANT: set GPG_TTY so GPG can prompt for passphrase |
| export GPG_TTY=$(tty) |
| ``` |
| |
| Note: all commands below use `uv run` which automatically activates the `.venv` environment. |
| If you prefer, you can instead `source .venv/bin/activate` and omit the `uv run` prefix. |
| |
| ## Building a Release |
| |
| Set these variables for the release you're building: |
| |
| ```bash |
| export VERSION=1.90.0 |
| export RC=0 |
| export PACKAGE_KEY=hamilton # one of: hamilton, sdk, lsp, contrib, ui |
| export APACHE_ID=your_apache_id |
| ``` |
| |
| The main release script is `scripts/apache_release_helper.py`: |
| |
| ```bash |
| uv run python scripts/apache_release_helper.py \ |
| --package ${PACKAGE_KEY} ${VERSION} ${RC} ${APACHE_ID} |
| ``` |
| |
| The script will: |
| 1. Check prerequisites (`flit`, `gpg`, `svn`) |
| 2. Validate the version in the source matches the version you specified |
| 3. Create a git tag (e.g., `apache-hamilton-v${VERSION}-incubating-RC${RC}`) |
| 4. Build the sdist (`.tar.gz`) and wheel (`.whl`) using `flit build --no-use-vcs` |
| 5. Validate the wheel with `twine check` |
| 6. Sign all artifacts with GPG and generate SHA512 checksums |
| 7. Upload to Apache SVN dist/dev |
| 8. Print a vote email template |
| |
| Output lands in the `dist/` directory under the package's working directory. |
| |
| ### Dry Run |
| |
| To build and sign artifacts without uploading to SVN or creating a git tag: |
| |
| ```bash |
| uv run python scripts/apache_release_helper.py \ |
| --package ${PACKAGE_KEY} ${VERSION} ${RC} ${APACHE_ID} --no-sign |
| ``` |
| |
| ### After the Vote Passes |
| |
| Once the vote passes, follow these steps to finalize the release. |
| If you don't have the variables from the build step, set them: |
| |
| ```bash |
| export VERSION=1.90.0 |
| export RC=0 |
| export PACKAGE_KEY=hamilton |
| export APACHE_ID=your_apache_id |
| ``` |
| |
| Derived variables used below: |
| |
| ```bash |
| export PACKAGE=apache-${PACKAGE_KEY} # e.g., apache-hamilton |
| export TAG="${PACKAGE}-v${VERSION}-incubating-RC${RC}" |
| ``` |
| |
| #### 1. Promote RC artifacts to the release SVN |
| |
| ```bash |
| # Dry run first to verify |
| scripts/promote_rc.sh --no-sign ${PACKAGE} ${VERSION} ${RC} |
| |
| # Then promote for real |
| scripts/promote_rc.sh ${PACKAGE} ${VERSION} ${RC} |
| ``` |
| |
| #### 2. Upload to PyPI |
| |
| ```bash |
| PACKAGE_UNDERSCORE=$(echo ${PACKAGE} | tr '-' '_') |
| twine upload dist/${PACKAGE_UNDERSCORE}-${VERSION}-py3-none-any.whl dist/${PACKAGE}-${VERSION}-incubating-src.tar.gz |
| ``` |
| |
| #### 3. Build and upload the sf-hamilton redirect package |
| |
| The `sf-hamilton` package on PyPI is a thin redirect that depends on `apache-hamilton`. |
| This ensures existing users who `pip install sf-hamilton` get the new release, and that |
| all extras (e.g., `sf-hamilton[dask]`, `sf-hamilton[mcp]`) continue to work. |
| |
| The `sf-hamilton-redirect/` directory contains a `pyproject.toml.template` with `VERSION` |
| as a placeholder. The build script stamps in the real version, builds, and validates: |
| |
| ```bash |
| sf-hamilton-redirect/build.sh ${VERSION} |
| |
| # Upload |
| twine upload sf-hamilton-redirect/dist/sf_hamilton-${VERSION}* |
| ``` |
| |
| #### 4. Sanity check the PyPI uploads |
| |
| ```bash |
| # Test apache-hamilton in a fresh env |
| pip install apache-hamilton==${VERSION} |
| python -c "import hamilton; print(hamilton.version.VERSION)" |
| |
| # Test sf-hamilton redirect (base + an extra) |
| pip install sf-hamilton[visualization]==${VERSION} |
| python -c "import hamilton; import graphviz; print('OK')" |
| ``` |
| |
| #### 5. Send announcement emails |
| |
| ```bash |
| python scripts/generate_announce_email.py \ |
| --package ${PACKAGE_KEY} --version ${VERSION} --rc ${RC} \ |
| --tag ${TAG} \ |
| --binding-votes 3 --nonbinding-votes 1 |
| ``` |
| |
| This generates three outputs: |
| - **[RESULT][VOTE]** email for `dev@hamilton.apache.org` |
| - **[ANNOUNCE]** email for `user@hamilton.apache.org` |
| - **Slack message** for copy-paste |
| |
| #### 6. Squash-merge the release branch back to main |
| |
| ```bash |
| git checkout main |
| git merge --squash release/hamilton/${VERSION} |
| git commit -m "Release apache-hamilton ${VERSION}" |
| git push origin main |
| ``` |
| |
| #### 7. Create the final (non-RC) release tag |
| |
| The RC build created an `-incubating-RC${RC}` tag pointing at the commit the |
| artifacts were built from (typically on the release branch). After the vote |
| passes and the release branch is squash-merged to main (step 6), create a |
| clean, RC-less release tag on the merge commit. This keeps the release tag |
| reachable from `main` and gives GitHub release pages a sensible name -- |
| ASF mentors flag `-RC`-suffixed tags as poor release names. |
| |
| `scripts/tag_release.sh` creates an annotated `<package>-v<version>-incubating` |
| tag on the target commit (default: current `HEAD`, i.e. the squash-merge |
| commit on main). The original `-RC` tag is left intact as the record of |
| exactly what was voted on. |
| |
| ```bash |
| # On main, at the squash-merge commit from step 6: |
| scripts/tag_release.sh --push ${PACKAGE} ${VERSION} |
| |
| # Or tag a specific commit / hold off on pushing: |
| scripts/tag_release.sh --commit <sha> ${PACKAGE} ${VERSION} # creates locally |
| git push origin refs/tags/${PACKAGE}-v${VERSION}-incubating # push when ready |
| ``` |
| |
| For a multi-package release (e.g. the apache-hamilton-{sdk,lsp,ui,contrib} |
| sub-packages), run it once per package against the same merge commit: |
| |
| ```bash |
| scripts/tag_release.sh --push apache-hamilton-sdk 0.9.0 |
| scripts/tag_release.sh --push apache-hamilton-lsp 0.2.0 |
| scripts/tag_release.sh --push apache-hamilton-ui 0.0.18 |
| scripts/tag_release.sh --push apache-hamilton-contrib 0.0.9 |
| ``` |
| |
| # For Voters: Verifying a Release |
| |
| If you're voting on a release, you can either use the automated script or follow the |
| manual steps below. |
| |
| ## Automated Verification |
| |
| The `scripts/verification-script.sh` script runs all verification steps end-to-end: |
| downloads from SVN, verifies signatures/checksums, checks license headers, builds |
| from source, runs tests, and exercises examples. |
| |
| ```bash |
| scripts/verification-script.sh <version> <rc> |
| # e.g., scripts/verification-script.sh 1.90.0 0 |
| ``` |
| |
| ## Manual Verification |
| |
| ### Step 1: Download the Artifacts |
| |
| ```bash |
| # Set version and RC number |
| export VERSION=1.90.0 # adjust to the version being voted on |
| export RC=0 |
| export PACKAGE=apache-hamilton # or apache-hamilton-sdk, etc. |
| |
| # Derived names (dashes for tarball, underscores for wheel) |
| export SRC_TAR=${PACKAGE}-${VERSION}-incubating-src.tar.gz |
| export WHEEL_NAME=$(echo ${PACKAGE} | tr '-' '_')-${VERSION}-py3-none-any.whl |
| export EXTRACTED_DIR=$(echo ${PACKAGE} | tr '-' '_')-${VERSION} |
| |
| # Download all artifacts from SVN |
| svn export https://dist.apache.org/repos/dist/dev/incubator/hamilton/${PACKAGE}/${VERSION}-RC${RC}/ hamilton-rc${RC} |
| cd hamilton-rc${RC} |
| |
| # Import the KEYS file |
| wget https://downloads.apache.org/incubator/hamilton/KEYS |
| gpg --import KEYS |
| ``` |
| |
| ### Step 2: Extract and Set Up |
| |
| ```bash |
| # Extract the source archive |
| tar -xzf ${SRC_TAR} |
| cd ${EXTRACTED_DIR}/ |
| |
| # Create a fresh environment and install build tools |
| uv venv --python 3.11 --clean |
| uv sync --group release |
| |
| # Download Apache RAT for license verification |
| curl -O https://repo1.maven.org/maven2/org/apache/rat/apache-rat/0.15/apache-rat-0.15.jar |
| ``` |
| |
| ### Step 3: Run Automated Verification |
| |
| The verification script checks GPG signatures, SHA512 checksums, and Apache license headers in one command. |
| The script looks for artifacts in a `dist/` directory by default, so first copy them there: |
| |
| ```bash |
| # Copy artifacts into dist/ so the verification script can find them |
| mkdir -p dist |
| cp ../${SRC_TAR}* dist/ |
| cp ../${WHEEL_NAME}* dist/ |
| |
| # Verify everything (signatures + checksums + license headers) |
| uv run python scripts/verify_apache_artifacts.py all --rat-jar apache-rat-0.15.jar |
| ``` |
| |
| You can also run individual checks: |
| |
| ```bash |
| # Signatures and checksums only |
| uv run python scripts/verify_apache_artifacts.py signatures |
| |
| # License headers only |
| uv run python scripts/verify_apache_artifacts.py licenses --rat-jar apache-rat-0.15.jar |
| |
| # Validate wheel metadata |
| uv run python scripts/verify_apache_artifacts.py twine-check |
| |
| # Inspect artifact contents |
| uv run python scripts/verify_apache_artifacts.py list-contents dist/${SRC_TAR} |
| uv run python scripts/verify_apache_artifacts.py list-contents dist/${WHEEL_NAME} |
| ``` |
| |
| ### Step 4: Build from Source |
| |
| ```bash |
| # Build the wheel from source |
| uv run flit build --no-use-vcs |
| |
| # Install the wheel you just built |
| uv pip install dist/${WHEEL_NAME} |
| ``` |
| |
| ### Step 5: Run Tests |
| |
| ```bash |
| # Install test dependencies |
| uv sync --group test |
| |
| # Run core unit tests |
| uv run pytest tests/ -x -q |
| |
| # Run plugin tests |
| # Note: some plugin tests require optional dependencies (ray, spark, vaex). |
| # Exclude any that are not installed in your environment: |
| uv run pytest plugin_tests/ -x -q \ |
| --ignore=plugin_tests/h_ray \ |
| --ignore=plugin_tests/h_spark \ |
| --ignore=plugin_tests/h_vaex |
| ``` |
| |
| ### Step 6: Run Examples |
| |
| The source archive includes representative examples to verify Hamilton works end-to-end. |
| |
| ```bash |
| # Hello World (no extra deps) |
| uv run python examples/hello_world/my_script.py |
| |
| # Data Quality with Pandera (must run from its directory for CSV data file) |
| cd examples/data_quality/simple |
| uv run python run.py |
| cd ../../.. |
| |
| # Function Reuse |
| uv run python examples/reusing_functions/main.py |
| |
| # Schema Validation |
| uv run python examples/schema/dataflow.py |
| |
| # Materialization (Pandas) |
| uv run python examples/pandas/materialization/my_script.py |
| ``` |
| |
| ### Manual Signature Verification (alternative to Step 3) |
| |
| If you prefer to verify signatures and checksums manually instead of using the verification script: |
| |
| ```bash |
| # From the hamilton-rc${RC}/ directory (before extracting) |
| |
| # Verify GPG signatures |
| gpg --verify ${SRC_TAR}.asc ${SRC_TAR} |
| gpg --verify ${WHEEL_NAME}.asc ${WHEEL_NAME} |
| |
| # Verify SHA512 checksums |
| # Note: the .sha512 files contain only the raw hash (no filename), |
| # so `shasum -c` won't work. Compare hashes manually instead: |
| echo "$(cat ${SRC_TAR}.sha512) ${SRC_TAR}" | shasum -a 512 -c - |
| echo "$(cat ${WHEEL_NAME}.sha512) ${WHEEL_NAME}" | shasum -a 512 -c - |
| ``` |
| |
| # Local Development |
| |
| For local wheel building/testing without signing or the full release process: |
| |
| ```bash |
| uv venv --python 3.11 |
| uv sync --group release |
| |
| # Build both sdist and wheel |
| uv run flit build --no-use-vcs |
| |
| # Or just the wheel |
| uv run flit build --no-use-vcs --format wheel |
| |
| # Install and test locally |
| uv pip install dist/apache_hamilton-*.whl |
| uv run python -c "import hamilton; print(hamilton.version.VERSION)" |
| ``` |