Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ Apache Hamilton was started at Stitch Fix before the original creators founded D
* [wren.ai](https://wren.ai/)

## 🤝 Code Contributors
[![Contributors](https://contrib.rocks/image?repo=dagworks-inc/hamilton)](https://github.com/apache/hamilton/graphs/contributors)
[![Contributors](https://contrib.rocks/image?repo=apache/hamilton)](https://github.com/apache/hamilton/graphs/contributors)


## 🙌 Special Mentions & 🦟 Bug Hunters
Expand Down
57 changes: 57 additions & 0 deletions docs/asf/downloads.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
================
Downloads
================

Official Apache Hamilton releases are available at the Apache Software Foundation distribution site.

Release Downloads
-----------------

All releases can be found at: `https://downloads.apache.org/incubator/hamilton/ <https://downloads.apache.org/incubator/hamilton/>`_

Each release includes:

- Source distributions (.tar.gz, .zip)
- Checksums (SHA512)
- Digital signatures (.asc)

Verifying Releases
------------------

To verify the integrity of downloaded files, you can:

1. Verify the checksum matches the published SHA512 sum
2. Verify the GPG signature using the KEYS file available at the downloads site

Release History
---------------

Visit the `downloads directory <https://downloads.apache.org/incubator/hamilton/>`_ to see all available releases.

Installation
------------

After downloading and verifying a release, you can install it using pip:

.. code-block:: bash

pip install apache-hamilton-<version>.tar.gz

Or install directly from PyPI:

.. code-block:: bash

pip install apache-hamilton

For more information about installation options, see the :doc:`Get Started guide </get-started/index>`.

Official Releases
-----------------

The following are the official Apache Hamilton releases:

- **1.89.0** (2025-10-11) - First Apache Hamilton release

- `Source (tar.gz) <https://downloads.apache.org/incubator/hamilton/1.89.0/apache-hamilton-1.89.0-incubating.tar.gz>`_
- `Checksums <https://downloads.apache.org/incubator/hamilton/1.89.0/apache-hamilton-1.89.0-incubating.tar.gz.sha512>`_
- `Signature <https://downloads.apache.org/incubator/hamilton/1.89.0/apache-hamilton-1.89.0-incubating.tar.gz.asc>`_
2 changes: 2 additions & 0 deletions docs/asf/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Apache Software Foundation links.
:glob:
:hidden:

downloads
Apache Software Foundation <https://www.apache.org/>
License <https://www.apache.org/licenses/>
Events <https://www.apache.org/events/current-event.html>
Expand All @@ -19,6 +20,7 @@ Apache Software Foundation links.
Thanks <https://www.apache.org/foundation/thanks.html>
Code of Conduct <https://www.apache.org/foundation/policies/conduct.html>

- :doc:`Downloads <downloads>`
- `Foundation <https://www.apache.org/>`_
- `License <https://www.apache.org/licenses/>`_
- `Events <https://www.apache.org/events/current-event.html>`_
Expand Down
7 changes: 7 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ integrations/index
code-comparisons/index
```

```{toctree}
:hidden: True
:caption: DOWNLOADS

asf/downloads
```

```{toctree}
:hidden: True
:caption: PDF
Expand Down
20 changes: 18 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,27 @@ requires = ["flit_core >=3.11,<4"]
build-backend = "flit_core.buildapi"

[project]
name = "sf-hamilton"
name = "apache-hamilton"
version = "1.89.0" # NOTE: keep this in sync with hamilton/version.py
# TODO: flip back to dynamic once hamilton version is a string. Flit doesn't handle tuples.
# dynamic = ["version"]
description = "Hamilton, the micro-framework for creating dataframes."
description = """Apache Hamilton (incubating) is a lightweight Python library for directed acyclic graphs (DAGs)
of transformations. Your DAG is **portable**; it runs anywhere Python runs, whether it's a script,
notebook, Airflow pipeline, FastAPI server, etc. Your DAG is **expressive**; Apache Hamilton has extensive
features to define and modify the execution of a DAG (e.g., data validation, experiment tracking, remote
execution).

Apache Hamilton (incubating) is an effort undergoing incubation at the Apache
Software Foundation (ASF), sponsored by the Apache Incubator PMC.

Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.

While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.
"""
readme = "README.md"
requires-python = ">=3.10.1, <4"
license = {text = "Apache-2.0"}
Expand Down
114 changes: 98 additions & 16 deletions scripts/apache_release_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import shutil
import subprocess
import sys
import tarfile
import tempfile
import zipfile

# --- Configuration ---
# You need to fill these in for your project.
Expand Down Expand Up @@ -138,10 +141,96 @@ def sign_artifacts(archive_name: str) -> list[str] | None:
return files


def _modify_wheel_for_apache_release(original_wheel: str, new_wheel_path: str):
"""Helper to modify the wheel for apache release.

# Flit somehow builds something incorrectly.
# 1. change PKG-INFO's first line to be `Metadata-Version: 2.4`
# 2. make sure the second line is `Name: apache-hamilton`
# 3. remove the `Import-Name: hamilton` line from PKG-INFO.

:param original_wheel: Path to the original wheel.
:param new_wheel_path: Path to the new wheel to create.
"""
with tempfile.TemporaryDirectory() as tmpdir:
# Unzip the wheel
with zipfile.ZipFile(original_wheel, "r") as zip_ref:
zip_ref.extractall(tmpdir)

# Find the .dist-info directory
dist_info_dirs = glob.glob(os.path.join(tmpdir, "*.dist-info"))
if not dist_info_dirs:
raise ValueError(f"Could not find .dist-info directory in {original_wheel}")
dist_info_dir = dist_info_dirs[0]
pkg_info = os.path.join(dist_info_dir, "PKG-INFO")

_modify_pkg_info_file(pkg_info)

# Create the new wheel
with zipfile.ZipFile(new_wheel_path, "w", zipfile.ZIP_DEFLATED) as zip_ref:
for root, _, files in os.walk(tmpdir):
for file in files:
zip_ref.write(
os.path.join(root, file), os.path.relpath(os.path.join(root, file), tmpdir)
)


def _modify_pkg_info_file(pkg_info_path: str):
"""
Flit somehow builds something incorrectly.
1. change PKG-INFO's first line to be `Metadata-Version: 2.4`
2. make sure the second line is `Name: apache-hamilton`
3. remove the `Import-Name: hamilton` line from PKG-INFO.
"""
with open(pkg_info_path, "r") as f:
lines = f.readlines()

new_lines = []
for i, line in enumerate(lines):
if i == 0:
new_lines.append("Metadata-Version: 2.4\n")
elif i == 1:
new_lines.append("Name: apache-hamilton\n")
elif line.strip() == "Import-Name: hamilton":
continue # Skip this line
else:
new_lines.append(line)

with open(pkg_info_path, "w") as f:
f.writelines(new_lines)


def _modify_tarball_for_apache_release(original_tarball: str, new_tarball_path: str):
"""Helper to modify the tarball for apache release.

# Flit somehow builds something incorrectly.
# 1. change PKG-INFO's first line to be `Metadata-Version: 2.4`
# 2. make sure the second line is `Name: apache-hamilton`
# 3. remove the `Import-Name: hamilton` line from PKG-INFO.

:param original_tarball: Path to the original tarball.
:param new_tarball_path: Path to the new tarball to create.
"""
with tempfile.TemporaryDirectory() as tmpdir:
# Extract the tarball
with tarfile.open(original_tarball, "r:gz") as tar:
tar.extractall(path=tmpdir)

# Modify the PKG-INFO file
# The extracted tarball has a single directory inside.
extracted_dir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
pkg_info_path = os.path.join(extracted_dir, "PKG-INFO")

_modify_pkg_info_file(pkg_info_path)

# Create the new tarball
with tarfile.open(new_tarball_path, "w:gz") as tar:
tar.add(extracted_dir, arcname=os.path.basename(extracted_dir))


def create_release_artifacts(version) -> list[str]:
"""Creates the source tarball, GPG signature, and checksums using `python -m build`."""
print("Creating release artifacts with 'python -m build'...")
files_to_upload = []
print("Creating release artifacts with 'flit build'...")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove the print and use echo in the .sh script instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is closer to the code -- so no difference?

# Clean the dist directory before building.
if os.path.exists("dist"):
shutil.rmtree("dist")
Expand All @@ -161,8 +250,7 @@ def create_release_artifacts(version) -> list[str]:
return None

# Find the created tarball in the dist directory.
expected_tar_ball = f"dist/sf_hamilton-{version.lower()}.tar.gz"
files_to_upload.append(expected_tar_ball)
expected_tar_ball = f"dist/apache-hamilton-{version.lower()}.tar.gz"
tarball_path = glob.glob(expected_tar_ball)

if not tarball_path:
Expand All @@ -179,29 +267,23 @@ def create_release_artifacts(version) -> list[str]:

# copy the tarball to be apache-hamilton-{version.lower()}-incubating.tar.gz
new_tar_ball = f"dist/apache-hamilton-{version.lower()}-incubating.tar.gz"
shutil.copy(tarball_path[0], new_tar_ball)
# shutil.copy(tarball_path[0], new_tar_ball)
_modify_tarball_for_apache_release(tarball_path[0], new_tar_ball)
archive_name = new_tar_ball
print(f"Found source tarball: {archive_name}")
main_signed_files = sign_artifacts(archive_name)
if main_signed_files is None:
new_tar_ball_singed = sign_artifacts(archive_name)
if new_tar_ball_singed is None:
raise ValueError("Could not sign the main release artifacts.")
# create sf-hamilton release artifacts
sf_hamilton_signed_files = sign_artifacts(expected_tar_ball)
# create wheel release artifacts
expected_wheel = f"dist/sf_hamilton-{version.lower()}-py3-none-any.whl"
expected_wheel = f"dist/apache-hamilton-{version.lower()}-py3-none-any.whl"
wheel_path = glob.glob(expected_wheel)
wheel_signed_files = sign_artifacts(wheel_path[0])
# create incubator wheel release artifacts
expected_incubator_wheel = f"dist/apache-hamilton-{version.lower()}-incubating-py3-none-any.whl"
shutil.copy(wheel_path[0], expected_incubator_wheel)
incubator_wheel_signed_files = sign_artifacts(expected_incubator_wheel)
files_to_upload = (
[new_tar_ball]
+ main_signed_files
+ [expected_tar_ball]
+ sf_hamilton_signed_files
+ [expected_wheel]
+ wheel_signed_files
+ new_tar_ball_singed
+ [expected_incubator_wheel]
+ incubator_wheel_signed_files
)
Expand Down
107 changes: 107 additions & 0 deletions scripts/promote_rc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/bin/bash
# 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.

# This script moves a release candidate from the dev repository to the release repository.

set -e

DRY_RUN=false
POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift # past argument
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done

set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters

if [ "$#" -ne 2 ]; then
echo "Usage: $0 [--dry-run] <version> <rc_num>"
echo "Example: $0 --dry-run 1.2.3 0"
exit 1
fi

VERSION=$1
RC_NUM=$2
PROJECT_SHORT_NAME="hamilton"

# Source and destination URLs
SOURCE_URL="https://dist.apache.org/repos/dist/dev/incubator/${PROJECT_SHORT_NAME}/${VERSION}-RC${RC_NUM}"
DEST_URL="https://dist.apache.org/repos/dist/release/incubator/${PROJECT_SHORT_NAME}/${VERSION}"

if [ "$DRY_RUN" = true ]; then
echo "Performing a dry run. No changes will be made."
else
echo "This script will copy the release candidate from dev to release."
echo "Source: ${SOURCE_URL}"
echo "Destination: ${DEST_URL}"
echo " "

read -p "Are you sure you want to continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
echo "Aborting."
exit 1
fi
fi

# Create the release directory
if [ "$DRY_RUN" = true ]; then
echo "DRY RUN: svn mkdir --parents -m \"Creating directory for Apache ${PROJECT_SHORT_NAME} ${VERSION}\" \"${DEST_URL}\""
else
svn mkdir --parents -m "Creating directory for Apache ${PROJECT_SHORT_NAME} ${VERSION}" "${DEST_URL}"
fi

# Get the list of files in the source directory
FILES=$(svn list "${SOURCE_URL}")

for FILE in $FILES; do
if [[ "$FILE" == apache-hamilton* ]]; then
SOURCE_FILE_URL="${SOURCE_URL}/${FILE}"
DEST_FILE_URL="${DEST_URL}/${DEST_FILE_NAME}"

if [ "$DRY_RUN" = true ]; then
echo "DRY RUN: svn mv \"${SOURCE_FILE_URL}\" \"${DEST_FILE_URL}\" -m \"Promote Apache ${PROJECT_SHORT_NAME} ${VERSION}: ${DEST_FILE_NAME}\""
else
echo "Moving ${FILE} to ${DEST_FILE_URL}"
svn mv "${SOURCE_FILE_URL}" "${DEST_FILE_URL}" -m "Promote Apache ${PROJECT_SHORT_NAME} ${VERSION}: ${DEST_FILE_NAME}"
fi
fi
done


if [ $? -eq 0 ]; then
if [ "$DRY_RUN" = true ]; then
echo "Dry run complete."
else
echo "Successfully copied release artifacts to: ${DEST_URL}"
echo "The release is now live."
fi
else
echo "Error: Failed to copy release artifacts. Please check the SVN URLs and your credentials."
exit 1
fi
Loading