Skip to content
Draft
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
5 changes: 3 additions & 2 deletions .github/workflows/contrib-auto-build-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: Publish SF Hamilton Contrib Python Package
on:
push:
branches:
- main # or the branch of your choice
- main
- 'release/contrib/**'
paths:
- 'contrib/**'

Expand Down Expand Up @@ -52,7 +53,7 @@ jobs:
python -m build
# Use the trusted publisher set up to remove the need for tokens
- name: Publish package distributions to PyPI
if: steps.check_version.outputs.version_is_greater == 'true'
if: steps.check_version.outputs.version_is_greater == 'true' && github.ref == 'refs/heads/main'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: contrib/dist/
3 changes: 2 additions & 1 deletion .github/workflows/hamilton-lsp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: LSP Test Workflow
on:
push:
branches:
- main # or any specific branches you want to include
- main
- 'release/lsp/**'
paths:
- 'dev_tools/language_server/**'

Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/hamilton-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ name: Unit Tests
on:
workflow_dispatch:

push:
branches:
- 'release/hamilton/**'
paths:
- '.github/**'
- 'hamilton/**'
- 'plugin_tests/**'
- 'tests/**'
- 'pyproject.toml'

pull_request:
branches:
- main
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/hamilton-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: SDK Test Workflow
on:
push:
branches:
- main # or any specific branches you want to include
- main
- 'release/sdk/**'
paths:
- 'ui/sdk/**'

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/hamilton-ui-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: Backend Test Workflow
on:
push:
branches:
- main # or any specific branches you want to include
- main
- 'release/ui/**'
paths:
- 'ui/backend/**'

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/hamilton-ui-frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ on:
push:
branches:
- main
- 'release/ui/**'
paths:
- 'ui/frontend/**'
pull_request:
Expand Down
90 changes: 74 additions & 16 deletions scripts/apache_release_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,22 +502,62 @@ def generate_email_template(package_name: str, version, rc_num, svn_url):
print("=" * 80)


def ensure_on_main():
"""Ensure we're on the main branch before starting a release."""
branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode().strip()
if branch != "main":
print(f"Error: Must be on 'main' branch to start a release, currently on '{branch}'.")
sys.exit(1)
print("Confirmed on 'main' branch.")


def ensure_clean_working_tree():
"""Ensure there are no uncommitted changes to tracked files."""
unstaged = subprocess.run(["git", "diff", "--quiet"], capture_output=True)
staged = subprocess.run(["git", "diff", "--cached", "--quiet"], capture_output=True)
if unstaged.returncode != 0 or staged.returncode != 0:
print("Error: Working tree has uncommitted changes to tracked files.")
print("Commit or stash them before creating a release.")
sys.exit(1)
print("Working tree is clean.")


def create_release_branch(package_key, version):
"""Create and checkout a release branch from main."""
branch_name = f"release/{package_key}/{version}"
try:
existing = (
subprocess.check_output(["git", "branch", "--list", branch_name]).decode().strip()
)
if existing:
print(f"Error: Release branch '{branch_name}' already exists.")
print("Delete it first if you want to start over: git branch -D " + branch_name)
sys.exit(1)
subprocess.run(["git", "checkout", "-b", branch_name], check=True)
print(f"Created and switched to release branch: {branch_name}")
return branch_name
except subprocess.CalledProcessError as e:
print(f"Error creating release branch: {e}")
sys.exit(1)


def main():
"""
### How to Use the Updated Script
### How to Use the Release Script

1. **Install the `flit` module**:
```bash
pip install flit
```
2. **Configure the Script**: The script now supports multiple Hamilton packages.
2. **Configure the Script**: The script supports multiple Hamilton packages.
Available packages: hamilton, sdk, lsp, contrib, ui
3. **Prerequisites**:
* You must be on the 'main' branch with a clean working tree.
* You must have `git`, `gpg`, `svn`, and the `flit` Python module installed.
* Your GPG key and SVN access must be configured for your Apache ID.
4. **Run the Script**:
Open your terminal, navigate to the root of your project directory, and run the script
with the desired package, version, release candidate number, and Apache ID.
Open your terminal, navigate to the root of your project directory, and run the script.
The script will create a release branch `release/<package>/<version>` from main.

Note: if you have multiple gpg keys, specify the default in ~/.gnupg/gpg.conf add a line with `default-key <KEYID>`.

Expand All @@ -543,7 +583,7 @@ def main():
parser.add_argument(
"--dry-run",
action="store_true",
help="Build and sign artifacts but skip git tagging and SVN upload.",
help="Build and sign artifacts but skip git tagging, branch push, and SVN upload.",
)
args = parser.parse_args()

Expand All @@ -561,22 +601,28 @@ def main():
print(f"{'=' * 80}\n")

check_prerequisites()
ensure_on_main()
ensure_clean_working_tree()

# Create release branch from main
release_branch = create_release_branch(package_key, version)

# Validate version matches what's in the version file
current_version = get_version_from_file(package_config)
print(f"Current version in {package_config['version_file']}: {current_version}")
if current_version != version:
print("Update the version in the version file to match the expected version.")
print(f"Switching back to main (release branch '{release_branch}' left for cleanup).")
subprocess.run(["git", "checkout", "main"], check=True)
sys.exit(1)

# Create git tag (from repo root)
# Create git tag on the release branch
tag_name = f"{package_name}-v{version}-incubating-RC{rc_num}"
if args.dry_run:
print(f"\n[dry-run] Skipping git tag creation: {tag_name}")
else:
print(f"\nChecking for git tag '{tag_name}'...")
try:
# Check if the tag already exists
existing_tag = subprocess.check_output(["git", "tag", "-l", tag_name]).decode().strip()
if existing_tag == tag_name:
print(f"Git tag '{tag_name}' already exists.")
Expand All @@ -587,7 +633,6 @@ def main():
print("Aborting.")
sys.exit(1)
else:
# Tag does not exist, create it
print(f"Creating git tag '{tag_name}'...")
subprocess.run(["git", "tag", tag_name], check=True)
print(f"Git tag {tag_name} created.")
Expand All @@ -604,20 +649,32 @@ def main():
sys.exit(1)

if args.dry_run:
# Dry run: skip SVN upload, show summary
print(f"\n{'=' * 80}")
print(" [dry-run] Skipping SVN upload")
print(" [dry-run] Skipping branch push and SVN upload")
print(f"{'=' * 80}\n")
print("Artifacts built successfully:")
for f in files_to_upload:
print(f" {f}")
print("\nTo do a real release, re-run without --dry-run.")
print(f"\nRelease branch: {release_branch}")
print("To do a real release, delete this branch and re-run without --dry-run.")
else:
# Upload artifacts
# Push release branch and tag
print(f"\n{'=' * 80}")
print(" Pushing Release Branch and Tag")
print(f"{'=' * 80}\n")
try:
subprocess.run(["git", "push", "-u", "origin", release_branch], check=True)
print(f"Pushed release branch: {release_branch}")
subprocess.run(["git", "push", "origin", tag_name], check=True)
print(f"Pushed tag: {tag_name}")
except subprocess.CalledProcessError as e:
print(f"Error pushing to remote: {e}")
sys.exit(1)

# Upload artifacts to SVN
print(f"\n{'=' * 80}")
print(" Uploading to Apache SVN")
print(f"{'=' * 80}\n")
# NOTE: You MUST have your SVN client configured to use your Apache ID and have permissions.
svn_url = svn_upload(package_name, version, rc_num, files_to_upload, apache_id)
if not svn_url:
sys.exit(1)
Expand All @@ -631,10 +688,11 @@ def main():
print("\n" + "=" * 80)
print(" Process Complete!")
print("=" * 80)
print(f"\nRelease branch: {release_branch}")
print(f"Tag: {tag_name}")
print("\nNext steps:")
print(f"1. Push the git tag: git push origin {tag_name}")
print("2. Copy the email template above and send to dev@hamilton.apache.org")
print("3. Wait for votes (minimum 72 hours)")
print("1. Copy the email template above and send to dev@hamilton.apache.org")
print("2. Wait for votes (minimum 72 hours)")
print("\n")


Expand Down
Loading