Skip to content
Open
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
107 changes: 107 additions & 0 deletions .rat-excludes
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# 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.

# ---------------------------------------------------------------------------
# Apache RAT exclusion list
# Patterns are Ant-style globs; one per line.
# Files listed here legitimately carry no Apache license header.
# ---------------------------------------------------------------------------

# Python bytecode and tool caches
**/__pycache__/**
**/*.pyc
**/*.pyo
.pytest_cache/**
.ruff_cache/**
*.egg-info/**

# Truly empty __init__.py namespace markers (0 bytes, no content to annotate).
# Non-empty __init__.py files carry a full license header and are checked by RAT.
solrorbit/builder/**/__init__.py
solrorbit/tools/__init__.py
solrorbit/visualizations/__init__.py
tests/builder/**/__init__.py
tests/unit/__init__.py

# Version / lock / pin files (no meaningful content to annotate)
version.txt
.python-version
docs/Gemfile.lock
docs/.ruby-version

# Configuration and data resource files shipped with the package
# (ASF policy does not require headers in pure config/data files)
**/*.ini
**/*.json
**/*.yml
**/*.yaml
**/*.j2
**/*.properties
**/*.options
**/*.xml

# Test fixtures and binary test data
tests/builder/data/**
tests/utils/resources/**
it/resources/**

# Binary and image files
**/*.svg
**/*.png
**/*.tar
**/*.tar.gz
**/*.tar.bz2
**/*.tgz
**/*.bz2
**/*.gz
**/*.zip
**/*.zst
**/*.crt
**/*.key

# Documentation (Markdown source and Jekyll-generated output)
**/*.md
docs/_site/**
docs/_includes/**
docs/_layouts/**
docs/_sass/**
docs/assets/**
docs/Gemfile

# GitHub and CI metadata files
.github/**
.ci/variables.json

# IDE project files
.idea/**

# Root-level project metadata
# (LICENSE and NOTICE are already excluded by RAT by default)
Makefile
pyproject.toml
MANIFEST.in

# Tool and infrastructure config files (no license header required)
.coveragerc
**/.gitignore
.asf.yaml
.fossa.yml
docker/Dockerfile
docker/.dockerignore

# Version files
solrorbit/min-version.txt
27 changes: 27 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Apache Solr Orbit.
- [Running Tests](#running-tests)
- [Submitting a Pull Request](#submitting-a-pull-request)
- [Developing Breaking Changes](#developing-breaking-changes)
- [Release](#release)
- [Miscellaneous](#miscellaneous)

## Prerequisites
Expand Down Expand Up @@ -122,6 +123,32 @@ make it
Develop breaking changes in a dedicated feature branch. Rebase onto `main`
before the next release and merge at that point.

## Release

### License audit (Apache RAT)

[Apache RAT](https://creadur.apache.org/rat/) is required by ASF release policy to verify
that every file in a source release carries an approved license header. Run it before
cutting a release:

```bash
make rat
```

RAT downloads its runner JAR to `~/.solr-orbit/cache/apache-rat/` on first use (requires
Python 3 and `java`). Exclusions for files that legitimately carry no header (test fixtures,
config files, generated output, binaries) are listed in `.rat-excludes`.

If RAT reports unexpected unapproved files, either:
- Add the Apache license header to the file, or
- Add an exclusion pattern to `.rat-excludes` with a comment explaining why the file is exempt

The full release pre-flight is run via:

```bash
make release-checks release_version=0.9.2 next_version=0.9.3
```

## Miscellaneous

### Avoiding secrets in commits
Expand Down
15 changes: 13 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ VERSIONS = $(shell jq -r '.python_versions | .[]' .ci/variables.json | sed '$$d'
VERSION312 = $(shell jq -r '.python_versions | .[]' .ci/variables.json | sed '$$d' | grep 3\.12)
PYENV_ERROR = "\033[0;31mIMPORTANT\033[0m: Please install pyenv and run \033[0;31meval \"\$$(pyenv init -)\"\033[0m.\n"

RAT_VERSION = 0.18
RAT_JAR_DIR = $(HOME)/.solr-orbit/cache/apache-rat
RAT_JAR = $(RAT_JAR_DIR)/apache-rat-$(RAT_VERSION).jar

all: develop

pyinst:
Expand Down Expand Up @@ -98,11 +102,18 @@ coverage:
coverage run -m pytest tests/
coverage html

$(RAT_JAR):
$(PYTHON) scripts/download-rat.py $(RAT_VERSION) $(RAT_JAR)

rat: $(RAT_JAR)
@java -version > /dev/null 2>&1 || { echo "ERROR: java not found on PATH; install a JDK to run Apache RAT"; exit 1; }
java -jar $(RAT_JAR) --input-exclude-file .rat-excludes -- .

release-checks:
./release-checks.sh $(release_version) $(next_version)
./scripts/release-checks.sh $(release_version) $(next_version)

# usage: e.g. make release release_version=0.9.2 next_version=0.9.3
release: release-checks clean it
./release.sh $(release_version) $(next_version)

.PHONY: install clean python-caches-clean tox-env-clean test it it312 benchmark coverage release release-checks pyinst
.PHONY: install clean python-caches-clean tox-env-clean lint format test it it312 benchmark coverage rat release release-checks pyinst
99 changes: 99 additions & 0 deletions scripts/download-rat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 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.

"""Download and verify the Apache RAT JAR.

Usage: python3 scripts/download-rat.py <version> <target_jar_path>

Downloads apache-rat-<version>-bin.tar.gz from the Apache mirror,
verifies its SHA-512 checksum, extracts the JAR, and writes it to
<target_jar_path>.
"""

import hashlib
import io
import sys
import tarfile
import urllib.error
import urllib.request
from pathlib import Path

PRIMARY_URL = "https://downloads.apache.org/creadur"
ARCHIVE_URL = "https://archive.apache.org/dist/creadur"


def download(url: str) -> bytes:
print(f"Downloading {url}", flush=True)
with urllib.request.urlopen(url) as resp:
return resp.read()


def download_with_fallback(primary: str, archive: str) -> bytes:
"""Try the primary mirror; fall back to the archive mirror on 404."""
try:
return download(primary)
except urllib.error.HTTPError as exc:
if exc.code != 404:
raise
print("Primary URL returned 404; trying archive mirror …", flush=True)
return download(archive)


def main() -> None:
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <version> <target_jar_path>", file=sys.stderr)
sys.exit(1)

version, target = sys.argv[1], Path(sys.argv[2])
tarball_name = f"apache-rat-{version}-bin.tar.gz"
subpath = f"apache-rat-{version}/{tarball_name}"
tar_url = f"{PRIMARY_URL}/{subpath}"
sha_url = f"{tar_url}.sha512"
tar_archive_url = f"{ARCHIVE_URL}/{subpath}"
sha_archive_url = f"{tar_archive_url}.sha512"

tarball = download_with_fallback(tar_url, tar_archive_url)
# Apache .sha512 files use coreutils format: "<hex> <filename>" or BSD
# format: "SHA512 (<filename>) = <hex>". Extract the bare hex token.
sha_content = download_with_fallback(sha_url, sha_archive_url).decode().strip()
if "=" in sha_content:
# BSD format
expected_hex = sha_content.split("=", 1)[1].strip()
else:
# GNU coreutils format: first whitespace-delimited token is the hash
expected_hex = sha_content.split()[0]

actual_hex = hashlib.sha512(tarball).hexdigest()
if actual_hex != expected_hex:
print(f"SHA-512 mismatch for {tarball_name}!", file=sys.stderr)
print(f" expected: {expected_hex}", file=sys.stderr)
print(f" actual: {actual_hex}", file=sys.stderr)
sys.exit(1)
print(f"SHA-512 verified: {tarball_name}", flush=True)

jar_member = f"apache-rat-{version}/apache-rat-{version}.jar"
with tarfile.open(fileobj=io.BytesIO(tarball), mode="r:gz") as tf:
member = tf.getmember(jar_member)
jar_data = tf.extractfile(member).read()

target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(jar_data)
print(f"JAR written to {target}", flush=True)


if __name__ == "__main__":
main()
36 changes: 36 additions & 0 deletions scripts/release-checks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env 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.

set -euo pipefail
cd "$(dirname "$0")/.."

release_version=${1:-}
next_version=${2:-}

if [ -z "$release_version" ] || [ -z "$next_version" ]; then
echo "Usage: $0 <release_version> <next_version>"
echo " e.g. $0 0.9.2 0.9.3"
exit 1
fi

echo "Running release checks for version ${release_version} (next: ${next_version})"

echo "--- Apache RAT license audit ---"
make rat

echo "All release checks passed for version ${release_version}."
22 changes: 22 additions & 0 deletions solrorbit/aggregator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# SPDX-License-Identifier: Apache-2.0
#
# Originally developed by OpenSearch Contributors; licensed under the Apache License, Version 2.0.
# License header was absent in the original source; added when adopted into Apache Solr Orbit.
# Modified by Apache Solr contributors; see git log for details.
#
# 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.
import os
import statistics
from typing import Any, Dict, List, Union
Expand Down
22 changes: 22 additions & 0 deletions solrorbit/builder/cluster_builder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# SPDX-License-Identifier: Apache-2.0
#
# Originally developed by OpenSearch Contributors; licensed under the Apache License, Version 2.0.
# License header was absent in the original source; added when adopted into Apache Solr Orbit.
# Modified by Apache Solr contributors; see git log for details.
#
# 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.
"""
The ClusterBuilder is the interface into the builder system from the Dispatcher. This class orchestrates all of the
builder subcomponents used to create and delete a cluster.
Expand Down
22 changes: 22 additions & 0 deletions solrorbit/builder/configs/listers/plugin_config_instance_lister.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# SPDX-License-Identifier: Apache-2.0
#
# Originally developed by OpenSearch Contributors; licensed under the Apache License, Version 2.0.
# License header was absent in the original source; added when adopted into Apache Solr Orbit.
# Modified by Apache Solr contributors; see git log for details.
#
# 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.
import logging
import os

Expand Down
Loading