From 2d92daec371b8ac5701c26c45c7f816f81d0c94f Mon Sep 17 00:00:00 2001 From: akotyla <79326805+akotyla@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:14:31 +0200 Subject: [PATCH] chore: Add package version update action (#78) --- .../pull_request_template.md | 4 + .github/workflows/semantic_release.yml | 67 +++++++++++ packages/ragbits-cli/CHANGELOG.md | 12 ++ packages/ragbits-core/CHANGELOG.md | 17 +++ packages/ragbits-document-search/CHANGELOG.md | 12 ++ packages/ragbits/CHANGELOG.md | 26 +++++ scripts/update_ragbits_package.py | 107 ++++++++++++++++-- 7 files changed, 233 insertions(+), 12 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md create mode 100644 .github/workflows/semantic_release.yml create mode 100644 packages/ragbits-cli/CHANGELOG.md create mode 100644 packages/ragbits-core/CHANGELOG.md create mode 100644 packages/ragbits-document-search/CHANGELOG.md create mode 100644 packages/ragbits/CHANGELOG.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 000000000..c3d778fb6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,4 @@ +### Checklist + +- [ ] I have updated the documentation accordingly. +- [ ] I have updated the CHANGELOG.md file accordingly. diff --git a/.github/workflows/semantic_release.yml b/.github/workflows/semantic_release.yml new file mode 100644 index 000000000..4a33bc7d1 --- /dev/null +++ b/.github/workflows/semantic_release.yml @@ -0,0 +1,67 @@ +name: Semantic Release + +on: + workflow_dispatch: + inputs: + updateType: + description: "version update type" + required: true + type: choice + default: "patch" + options: + - "major" + - "minor" + - "patch" + packageName: + description: "name of the package to update" + required: true + type: choice + options: + - "ragbits" + - "ragbits-cli" + - "ragbits-core" + - "ragbits-document-search" + +jobs: + release: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v2 + with: + version: "0.4.10" + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Create release branch + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b release/${{ github.event.inputs.packageName }}-$(date +%Y-%m-%d) + + - name: Update packages + id: packages_update + run: | + echo old_version=`grep version packages/${{ github.event.inputs.packageName }}/pyproject.toml | cut -d \" -f2` >> $GITHUB_OUTPUT + uv run scripts/update_ragbits_package.py ${{ github.event.inputs.packageName }} ${{ github.event.inputs.updateType }} + echo new_version=`grep version packages/${{ github.event.inputs.packageName }}/pyproject.toml | cut -d \" -f2` >> $GITHUB_OUTPUT + uv sync + + - name: Create PR with updated packages + run: | + COMMIT_MESSAGE="release(${{ github.event.inputs.packageName }}): update to v${{ steps.packages_update.outputs.new_version }}" + git add . + git commit -m "$COMMIT_MESSAGE" + git push -u origin HEAD + gh pr create -B main --title "$COMMIT_MESSAGE" \ + --body 'Update ${{ github.event.inputs.packageName }} version from ${{ steps.packages_update.outputs.old_version }} to ${{ steps.packages_update.outputs.new_version }}' + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/packages/ragbits-cli/CHANGELOG.md b/packages/ragbits-cli/CHANGELOG.md new file mode 100644 index 000000000..f3675ddde --- /dev/null +++ b/packages/ragbits-cli/CHANGELOG.md @@ -0,0 +1,12 @@ +# CHANGELOG + +## Unreleased + +## 0.1.0 (2024-10-08) + +### Added + +- Initial release of the package. +- Add prompts lab command. +- Add prompts generate-promptfoo-configs command. + diff --git a/packages/ragbits-core/CHANGELOG.md b/packages/ragbits-core/CHANGELOG.md new file mode 100644 index 000000000..0b8746e0b --- /dev/null +++ b/packages/ragbits-core/CHANGELOG.md @@ -0,0 +1,17 @@ +# CHANGELOG + +## Unreleased + +## 0.1.0 (2024-10-08) + +### Added + + +- Initial release of the package. +- Introduce core components: Prompts, LLMs, Embeddings and VectorStores. +- `Prompt` class integration with promptfoo. +- LiteLLM integration. +- ChromaDB integration. +- Prompts lab. +- Prompts autodiscovery. + diff --git a/packages/ragbits-document-search/CHANGELOG.md b/packages/ragbits-document-search/CHANGELOG.md new file mode 100644 index 000000000..28318ab8b --- /dev/null +++ b/packages/ragbits-document-search/CHANGELOG.md @@ -0,0 +1,12 @@ +# CHANGELOG + +## Unreleased + + +## 0.1.0 (2024-10-08) + +### Added + +- Initial release of the package. +- Introduce core modules: documents, ingestion and retrival. +- Unstructured integration. diff --git a/packages/ragbits/CHANGELOG.md b/packages/ragbits/CHANGELOG.md new file mode 100644 index 000000000..988a34848 --- /dev/null +++ b/packages/ragbits/CHANGELOG.md @@ -0,0 +1,26 @@ +# CHANGELOG + +## Unreleased + +## 0.1.0 (2024-10-08) + +### Added + +- ragbits-core v0.1.0: + - Initial release of the package. + - Introduce core components: Prompts, LLMs, Embeddings and VectorStores. + - `Prompt` class integration with promptfoo. + - LiteLLM integration. + - ChromaDB integration. + - Prompts lab. + - Prompts autodiscovery. + +- ragbits-cli v0.1.0: + - Initial release of the package. + - Add prompts lab command. + - Add prompts generate-promptfoo-configs command. + +- ragbits-document-search v0.1.0: + - Initial release of the package. + - Introduce core modules: documents, ingestion and retrival. + - Unstructured integration. diff --git a/scripts/update_ragbits_package.py b/scripts/update_ragbits_package.py index b78f42352..98177dc68 100644 --- a/scripts/update_ragbits_package.py +++ b/scripts/update_ragbits_package.py @@ -12,8 +12,9 @@ # # uv run scripts/update_ragbits_package.py # - +import re from copy import deepcopy +from datetime import datetime from enum import Enum from pathlib import Path from typing import Optional @@ -46,7 +47,7 @@ def _version_to_list(version_string): return [int(part) for part in version_string.split(".")] -def _check_update_type(version: str, new_version: str) -> Optional[UpdateType]: +def _check_update_type(version: str, new_version: str) -> UpdateType: version_list = _version_to_list(version) new_version_list = _version_to_list(new_version) @@ -54,9 +55,7 @@ def _check_update_type(version: str, new_version: str) -> Optional[UpdateType]: return UpdateType.MAJOR if version_list[1] != new_version_list[1]: return UpdateType.MINOR - if version_list[2] != new_version_list[2]: - return UpdateType.PATCH - return None + return UpdateType.PATCH def _get_updated_version(version: str, update_type: UpdateType) -> str: @@ -78,6 +77,7 @@ def _update_pkg_version( pkg_pyproject: Optional[tomlkit.TOMLDocument] = None, new_version: Optional[str] = None, update_type: Optional[UpdateType] = None, + sync_ragbits_version: bool = False, ) -> tuple[str, str]: if not pkg_pyproject: pkg_pyproject = tomlkit.parse((PACKAGES_DIR / pkg_name / "pyproject.toml").read_text()) @@ -97,9 +97,87 @@ def _update_pkg_version( assert isinstance(new_version, str) pprint(f"[green]The {pkg_name} package was successfully updated from {version} to {new_version}.[/green]") + if pkg_name != "ragbits": + _sync_ragbits_deps(pkg_name, version, new_version, sync_ragbits_version) + + _create_changelog_release(pkg_name=pkg_name, new_version=new_version) + return version, new_version +def _sync_ragbits_deps(pkg_name: str, pkg_version: str, pkg_new_version: str, update_version: bool = True): + ragbits_pkg_project = tomlkit.parse((PACKAGES_DIR / "ragbits" / "pyproject.toml").read_text()) + ragbits_deps: list[str] = [dep.split("==")[0] for dep in ragbits_pkg_project["project"]["dependencies"]] + + update_type = _check_update_type(pkg_version, pkg_new_version) + + if pkg_name in ragbits_deps: + idx = ragbits_pkg_project["project"]["dependencies"].index(f"{pkg_name}=={pkg_version}") + del ragbits_pkg_project["project"]["dependencies"][idx] + ragbits_pkg_project["project"]["dependencies"].insert(idx, f"{pkg_name}=={pkg_new_version}") + _add_updated_dependency_to_changelog("ragbits", pkg_name, pkg_new_version) + + if update_version: + ragbits_old_version = ragbits_pkg_project["project"]["version"] + ragbits_new_version = _get_updated_version(ragbits_old_version, update_type=update_type) + ragbits_pkg_project["project"]["version"] = ragbits_new_version + + pprint( + "[green]The ragbits package was successfully updated " + f"from {ragbits_old_version} to {ragbits_new_version}.[/green]" + ) + _create_changelog_release(pkg_name="ragbits", new_version=ragbits_new_version) + + (PACKAGES_DIR / "ragbits" / "pyproject.toml").write_text(tomlkit.dumps(ragbits_pkg_project)) + + +def _add_updated_dependency_to_changelog(pkg_name: str, dependency_name: str, new_dependency_version: str) -> None: + changelog_path = PACKAGES_DIR / pkg_name / "CHANGELOG.md" + changelog_content = changelog_path.read_text() + + # Find the "## Unreleased" section + unreleased_match = re.search(r"^## Unreleased\s*$", changelog_content, re.MULTILINE) + if unreleased_match: + unreleased_index = unreleased_match.end() + + # Find the next section after "## Unreleased" + next_section_match = re.search(r"^##\s", changelog_content[unreleased_index:], re.MULTILINE) + next_section_index = ( + unreleased_index + next_section_match.start() if next_section_match else len(changelog_content) + ) + + # Check if "### Changed" exists in the "## Unreleased" section + changed_match = re.search( + r"^### Changed\s*$", changelog_content[unreleased_index:next_section_index], re.MULTILINE + ) + if not changed_match: + # If "### Changed" does not exist, create it above any existing sections + changelog_content = ( + changelog_content[:unreleased_index] + + f"\n### Changed\n\n- {dependency_name} updated to version v{new_dependency_version}\n" + + changelog_content[unreleased_index:] + ) + else: + # If "### Changed" exists, append the new entry + changed_index = unreleased_index + changed_match.end() + changelog_content = ( + changelog_content[:changed_index] + + f"\n- {dependency_name} updated to version v{new_dependency_version}" + + changelog_content[changed_index:] + ) + + changelog_path.write_text(changelog_content) + + +def _create_changelog_release(pkg_name: str, new_version: str) -> None: + changelog_path = PACKAGES_DIR / pkg_name / "CHANGELOG.md" + changelog_content = changelog_path.read_text() + changelog_content = changelog_content.replace( + "## Unreleased", f"## Unreleased\n\n## {new_version} ({datetime.today().strftime('%Y-%m-%d')})" + ) + changelog_path.write_text(changelog_content) + + def run(pkg_name: Optional[str] = typer.Argument(None), update_type: Optional[str] = typer.Argument(None)) -> None: """ Main entry point for the package version updater. Updates package versions based on user input. @@ -130,10 +208,12 @@ def run(pkg_name: Optional[str] = typer.Argument(None), update_type: Optional[st pkg_name = list_input("Enter the package name", choices=packages) casted_update_type = _update_type_to_enum(update_type) - user_prompt_required = pkg_name is None or casted_update_type is None - if pkg_name == "ragbits-core": + if pkg_name == "ragbits": + _update_pkg_version(pkg_name, update_type=casted_update_type) + + elif pkg_name == "ragbits-core": if user_prompt_required: print("When upgrading the ragbits-core package it is also necessary to upgrade the other packages.") is_continue = confirm(message="Do you want to continue?") @@ -141,21 +221,24 @@ def run(pkg_name: Optional[str] = typer.Argument(None), update_type: Optional[st is_continue = True if is_continue: - ragbits_version, new_ragbits_version = _update_pkg_version(pkg_name, update_type=casted_update_type) - casted_update_type = _check_update_type(ragbits_version, new_ragbits_version) + version, new_version = _update_pkg_version(pkg_name, update_type=casted_update_type) + casted_update_type = _check_update_type(version, new_version) - for pkg in [pkg for pkg in packages if pkg != "ragbits-core"]: + for pkg in sorted([pkg for pkg in packages if pkg != "ragbits-core"], reverse=True): pkg_pyproject = tomlkit.parse((PACKAGES_DIR / pkg / "pyproject.toml").read_text()) pkg_pyproject["project"]["dependencies"] = [ dep for dep in pkg_pyproject["project"]["dependencies"] if "ragbits-core" not in dep ] - pkg_pyproject["project"]["dependencies"].append(f"ragbits-core=={new_ragbits_version}") + pkg_pyproject["project"]["dependencies"].append(f"ragbits-core=={new_version}") + if pkg != "ragbits": + _add_updated_dependency_to_changelog(pkg, pkg_name, new_version) _update_pkg_version(pkg, pkg_pyproject, update_type=casted_update_type) else: pprint("[red]The ragbits-core package was not successfully updated.[/red]") + else: - _update_pkg_version(pkg_name, update_type=casted_update_type) + _update_pkg_version(pkg_name, update_type=casted_update_type, sync_ragbits_version=True) if __name__ == "__main__":