This is an automated email from the ASF dual-hosted git repository. JingsongLi pushed a commit to branch add-contributing-ci-tools in repository https://gitbox.apache.org/repos/asf/paimon-vector-index.git
commit a1266d72e4e7fac336bea1588e06172bcebad497 Author: JingsongLi <[email protected]> AuthorDate: Mon Jun 8 14:48:20 2026 +0800 Add CONTRIBUTING.md, ASF yaml validator, and CI for JNI/Python Final batch from poc branch migration: - CONTRIBUTING.md: development workflow, code style, and PR guidelines - tools/validate_asf_yaml.py: validates .asf.yaml against ASF infra schema (supports rulesets, squash_commit_message fields) - CI enhancements: add .asf.yaml validation step, --workspace flag for coverage, JNI build job (JDK 8), Python build job (maturin) Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .github/workflows/ci.yml | 58 ++++++++++++++++- CONTRIBUTING.md | 53 ++++++++++++++++ tools/validate_asf_yaml.py | 155 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f54c513..1a47796 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Validate .asf.yaml + run: pip install pyyaml -q && python3 tools/validate_asf_yaml.py + - name: Install cargo-deny uses: taiki-e/install-action@v2 with: @@ -77,4 +80,57 @@ jobs: uses: taiki-e/install-action@cargo-llvm-cov - name: Test with coverage - run: cargo llvm-cov --summary-only + run: cargo llvm-cov --workspace --summary-only + + jni-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Rust Cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: '8' + distribution: 'temurin' + + - name: Build JNI library + run: cargo build -p paimon-vindex-jni --release + + python-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Rust Cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install maturin + run: pip install maturin + + - name: Build Python module + working-directory: python + run: maturin build --release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3bf3f7f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,53 @@ +<!-- + ~ 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. +--> + +# Contributing + +## Get Started + +This is a Rust project, so [rustup](https://rustup.rs/) is a great place to start. It provides an easy way to manage your Rust installation and toolchains. + +This is a pure Rust project, so only `cargo` is needed. Here are some common commands to get you started: + +- `cargo check`: Analyze the current package and report errors. +- `cargo fmt`: Format the current code according to the Rust style guidelines. +- `cargo build`: Compile the current package. +- `cargo clippy`: Catch common mistakes and improve code quality. +- `cargo test`: Run unit tests. +- `cargo bench`: Run benchmark tests. + +## Setting up the Development Environment + +1. Install Rust using `rustup`. Follow the instructions on the [rustup website](https://rustup.rs/) to install Rust on your system. +2. Clone the repository to your local machine. +3. Navigate to the project directory. + +## Making Changes + +1. Create a new branch for your changes. +2. Make your changes and ensure that the code still compiles and passes all tests. +3. Format your code using `cargo fmt` to ensure consistency with the project's code style. + +## Submitting Changes + +1. Once you are satisfied with your changes, push your branch to the remote repository. +2. Open a pull request on the project's GitHub page. Provide a clear description of your changes and why they are necessary. +3. Wait for reviews and address any feedback. Once the pull request is approved and merged, your changes will be part of the project. + +Thank you for contributing to this project! diff --git a/tools/validate_asf_yaml.py b/tools/validate_asf_yaml.py new file mode 100644 index 0000000..e01c7cc --- /dev/null +++ b/tools/validate_asf_yaml.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 + +# +# 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. +# + +"""Validate .asf.yaml against known ASF infrastructure schema. + +Reference: https://github.com/apache/infrastructure-asfyaml/blob/main/README.md +""" + +import sys + +try: + import yaml +except ImportError: + sys.exit( + "PyYAML is required: pip install pyyaml\n" + "Or use: python3 -c \"import pip; pip.main(['install', 'pyyaml'])\"" + ) + +ASF_YAML_PATH = ".asf.yaml" + +VALID_TOP_LEVEL_KEYS = { + "github", + "notifications", + "staging", + "publish", + "pelican", +} + +VALID_GITHUB_KEYS = { + "description", + "homepage", + "labels", + "features", + "enabled_merge_buttons", + "protected_branches", + "collaborators", + "autolinks", + "environments", + "dependabot_alerts", + "dependabot_updates", + "code_scanning", + "del_branch_on_merge", + "ghp_branch", + "ghp_path", + "rulesets", +} + +VALID_FEATURES_KEYS = { + "issues", + "discussions", + "wiki", + "projects", +} + +VALID_MERGE_BUTTON_KEYS = { + "squash", + "merge", + "rebase", + "squash_commit_message", +} + +VALID_NOTIFICATIONS_KEYS = { + "commits", + "issues", + "pullrequests", + "jira_options", + "jobs", + "discussions", +} + + +def validate(): + errors = [] + + try: + with open(ASF_YAML_PATH, "r") as f: + data = yaml.safe_load(f) + except FileNotFoundError: + print(f"SKIP: {ASF_YAML_PATH} not found") + return 0 + except yaml.YAMLError as e: + print(f"ERROR: Invalid YAML syntax in {ASF_YAML_PATH}: {e}") + return 1 + + if not isinstance(data, dict): + print(f"ERROR: {ASF_YAML_PATH} root must be a mapping") + return 1 + + for key in data: + if key not in VALID_TOP_LEVEL_KEYS: + errors.append(f"unexpected top-level key '{key}'") + + github = data.get("github") + if isinstance(github, dict): + for key in github: + if key not in VALID_GITHUB_KEYS: + errors.append(f"unexpected key 'github.{key}'") + + features = github.get("features") + if isinstance(features, dict): + for key in features: + if key not in VALID_FEATURES_KEYS: + errors.append( + f"unexpected key 'github.features.{key}' " + f"(allowed: {', '.join(sorted(VALID_FEATURES_KEYS))})" + ) + elif not isinstance(features[key], bool): + errors.append( + f"'github.features.{key}' must be a boolean, " + f"got {type(features[key]).__name__}" + ) + + merge_buttons = github.get("enabled_merge_buttons") + if isinstance(merge_buttons, dict): + for key in merge_buttons: + if key not in VALID_MERGE_BUTTON_KEYS: + errors.append( + f"unexpected key 'github.enabled_merge_buttons.{key}' " + f"(allowed: {', '.join(sorted(VALID_MERGE_BUTTON_KEYS))})" + ) + + notifications = data.get("notifications") + if isinstance(notifications, dict): + for key in notifications: + if key not in VALID_NOTIFICATIONS_KEYS: + errors.append(f"unexpected key 'notifications.{key}'") + + if errors: + print(f"ERROR: {ASF_YAML_PATH} validation failed:") + for err in errors: + print(f" - {err}") + return 1 + + print(f"OK: {ASF_YAML_PATH} is valid") + return 0 + + +if __name__ == "__main__": + sys.exit(validate())
