diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index e616d994fd..22ac6d1ac9 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -16,7 +16,7 @@ on: python-version: description: "Python version to test against" required: true - default: "3.10" + default: "3.12" type: string upgrade-deps: description: "Whether to install all extras" diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index f8b1ca163c..0b73a9d6fc 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -7,7 +7,9 @@ on: jobs: create-tag: - if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'chore/release-') + if: | + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.head.ref, 'chore/release-') runs-on: ubuntu-latest permissions: contents: write @@ -16,15 +18,23 @@ jobs: - name: Checkout uses: actions/checkout@v6 with: - ref: develop + ref: ${{ github.event.pull_request.merge_commit_sha }} fetch-depth: 0 - - name: Extract version from pyproject.toml + - name: Verify commit author is not a bot + run: | + AUTHOR=$(git log -1 --format='%ae' ${{ github.event.pull_request.merge_commit_sha }}) + echo "Merge commit author: ${AUTHOR}" + if echo "${AUTHOR}" | grep -qiE '(dependabot|bot@)'; then + echo "::error::Commit authored by a bot (${AUTHOR}), aborting tag creation." + exit 1 + fi + + - name: Derive tag from commit SHA id: version run: | - VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "tag=v${VERSION}" >> $GITHUB_OUTPUT + SHORT_SHA=$(git rev-parse --short ${{ github.event.pull_request.merge_commit_sha }}) + echo "tag=sha-${SHORT_SHA}" >> $GITHUB_OUTPUT - name: Check if tag already exists id: check-tag @@ -42,7 +52,7 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -a ${{ steps.version.outputs.tag }} -m "Release ${{ steps.version.outputs.tag }}" + git tag -a ${{ steps.version.outputs.tag }} ${{ github.event.pull_request.merge_commit_sha }} -m "Release ${{ steps.version.outputs.tag }}" git push origin ${{ steps.version.outputs.tag }} - name: Tag already exists diff --git a/.github/workflows/docs-build.yaml b/.github/workflows/docs-build.yaml deleted file mode 100644 index 41e69288a0..0000000000 --- a/.github/workflows/docs-build.yaml +++ /dev/null @@ -1,150 +0,0 @@ -name: docs-build - -on: - pull_request: - branches: [develop] - types: [opened, synchronize] - paths: - - "docs/**" - - "**/*.md" - - "**/*.rst" - push: - branches: [develop] - tags: - - docs-v* - - v* - workflow_dispatch: - -defaults: - run: - shell: bash - -env: - PYTHON_VERSION: "3.10" - POETRY_VERSION: "1.8.2" - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - build-docs: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION }} - - name: Bootstrap poetry (Linux and macOS) - run: | - curl -sSL https://install.python-poetry.org | POETRY_VERSION=${{ env.POETRY_VERSION }} python - - - name: Update PATH - run: echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Configure poetry - run: poetry config virtualenvs.in-project true - - name: Install dependencies - run: poetry install --with docs - - name: Validate redirect targets - run: make docs-check-redirects - - name: Build documentation - run: make docs-strict - - name: Delete unnecessary files - run: | - sudo find _build -name .doctrees -prune -exec rm -rf {} \; - sudo find _build -name .buildinfo -exec rm {} \; - - name: Upload HTML - uses: actions/upload-artifact@v6 - with: - name: html-build-artifact - path: _build/docs - if-no-files-found: error - retention-days: 1 - - name: Store PR information - if: github.event_name == 'pull_request' - run: | - mkdir ./pr - echo ${{ github.event.number }} > ./pr/pr.txt - echo ${{ github.event.pull_request.merged }} > ./pr/merged.txt - echo ${{ github.event.action }} > ./pr/action.txt - - name: Upload PR information - if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v6 - with: - name: pr - path: pr/ - - store-html: - if: github.event_name == 'push' && github.repository_owner == 'NVIDIA-NeMo' - needs: [build-docs] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - ref: "gh-pages" - - name: Initialize Git configuration - run: | - git config user.name docs-build - git config user.email do-not-send@github.com - - name: Download artifacts - uses: actions/download-artifact@v7 - with: - name: html-build-artifact - path: ${{ github.ref_name }} - - name: Copy HTML directories - run: | - ls -asl - for i in `ls -d *` - do - echo "Git adding ${i}" - git add "${i}" - done - - name: Check or create dot-no-jekyll file - run: | - if [ -f ".nojekyll" ]; then - echo "The dot-no-jekyll file already exists." - exit 0 - fi - touch .nojekyll - git add .nojekyll - - name: Check or create redirect page - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - resp=$(grep 'http-equiv="refresh"' index.html 2>/dev/null) || true - if [ -n "${resp}" ]; then - echo "The redirect file already exists." - exit 0 - fi - # If any of these commands fail, fail the build. - def_branch=$(gh api "repos/${GITHUB_REPOSITORY}" --jq ".default_branch") - html_url=$(gh api "repos/${GITHUB_REPOSITORY}/pages" --jq ".html_url") - # Beware ugly quotation mark avoidance in the foll lines. - echo '' > index.html - echo '' >> index.html - echo ' ' >> index.html - echo ' Redirect to documentation' >> index.html - echo ' ' >> index.html - echo ' ' >> index.html - echo ' ' >> index.html - echo ' ' >> index.html - echo ' ' >> index.html - echo ' ' >> index.html - echo '

Please follow the link to the ' >> index.html - echo ${def_branch}' branch documentation.

' >> index.html - echo ' ' >> index.html - echo '' >> index.html - git add index.html - - name: Commit changes to the GitHub Pages branch - run: | - git status - if git commit -m 'Pushing changes to GitHub Pages.'; then - git push -f - else - echo "Nothing changed." - fi diff --git a/.github/workflows/docs-preview-pr.yaml b/.github/workflows/docs-preview-pr.yaml deleted file mode 100644 index a91065b4ae..0000000000 --- a/.github/workflows/docs-preview-pr.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: docs-preview-pr - -on: - workflow_run: - workflows: [docs-build] - types: [completed] - branches-ignore: [develop] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - WF_ID: ${{ github.event.workflow_run.id }} - -jobs: - preview: - uses: nvidia-merlin/.github/.github/workflows/docs-preview-pr-common.yaml@main diff --git a/.github/workflows/docs-remove-stale-reviews.yaml b/.github/workflows/docs-remove-stale-reviews.yaml deleted file mode 100644 index eb1ee61d3f..0000000000 --- a/.github/workflows/docs-remove-stale-reviews.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: docs-remove-stale-reviews - -on: - schedule: - # 42 minutes after 0:00 UTC on Sundays - - cron: "42 0 * * 0" - workflow_dispatch: - -jobs: - remove: - uses: nvidia-merlin/.github/.github/workflows/docs-remove-stale-reviews-common.yaml@main diff --git a/.github/workflows/full-tests.yml b/.github/workflows/full-tests.yml index 56898a41c7..e818239bbb 100644 --- a/.github/workflows/full-tests.yml +++ b/.github/workflows/full-tests.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: os: [Windows, macOS] # exclude Ubuntu as it is available in pr-tests - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.12"] include: - os: Windows image: windows-2022 diff --git a/.github/workflows/latest-deps-tests.yml b/.github/workflows/latest-deps-tests.yml index 015ed8aa63..2fd3ac72c0 100644 --- a/.github/workflows/latest-deps-tests.yml +++ b/.github/workflows/latest-deps-tests.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [Ubuntu, macOS, Windows] - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.12"] include: - os: Ubuntu image: ubuntu-latest diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f0a3d4c6d1..40914d5060 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ on: env: POETRY_VERSION: 1.8.2 - PYTHON_VERSION: "3.11" + PYTHON_VERSION: "3.12" jobs: lint: diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 66f025ad01..947a35bf40 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -11,11 +11,11 @@ jobs: strategy: matrix: os: [Ubuntu] - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.12"] include: - os: Ubuntu image: ubuntu-latest - - python-version: "3.11" + - python-version: "3.12" with-coverage: true fail-fast: false uses: ./.github/workflows/_test.yml diff --git a/.github/workflows/publish-pypi-approval.yml b/.github/workflows/publish-pypi-approval.yml deleted file mode 100644 index 89f202709a..0000000000 --- a/.github/workflows/publish-pypi-approval.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: Publish to PyPI (with Approval) - -on: - workflow_run: - workflows: ["Build and Test Distribution"] - types: - - completed - -jobs: - publish-pypi: - if: github.event.workflow_run.conclusion == 'success' - runs-on: ubuntu-latest - environment: - name: pypi-production - url: https://pypi.org/project/nemoguardrails/ - permissions: - contents: write - id-token: write - - steps: - - name: Detect version tag from workflow event - id: version - run: | - HEAD_BRANCH="${{ github.event.workflow_run.head_branch }}" - - echo "Workflow triggered by: $HEAD_BRANCH" - - if [[ "$HEAD_BRANCH" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - TAG_NAME="$HEAD_BRANCH" - VERSION="${TAG_NAME#v}" - - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "tag=${TAG_NAME}" >> $GITHUB_OUTPUT - echo "artifact_name=${TAG_NAME}-build" >> $GITHUB_OUTPUT - - echo "✅ Detected version tag: $TAG_NAME" - echo " Version: $VERSION" - echo " Artifact: ${TAG_NAME}-build" - else - echo "❌ Not triggered by a version tag: $HEAD_BRANCH" - echo "This workflow should only run for version tags (vX.Y.Z)" - exit 1 - fi - - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ steps.version.outputs.tag }} - fetch-depth: 0 - - - name: Validate version matches tag - run: | - VERSION_IN_FILE=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') - TAG_VERSION="${{ steps.version.outputs.version }}" - if [ "$VERSION_IN_FILE" != "$TAG_VERSION" ]; then - echo "❌ Version mismatch: pyproject.toml=$VERSION_IN_FILE, tag=$TAG_VERSION" - exit 1 - fi - echo "✅ Version validated: $VERSION_IN_FILE matches tag $TAG_VERSION" - - - name: Download artifact - uses: actions/download-artifact@v7 - with: - name: ${{ steps.version.outputs.artifact_name }} - path: dist - github-token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.repository }} - run-id: ${{ github.event.workflow_run.id }} - - - name: List files - run: ls -la dist/ - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - verbose: true - packages-dir: dist/ - attestations: true - - - name: Create GitHub Release - env: - GH_TOKEN: ${{ github.token }} - run: | - TAG_NAME="${{ steps.version.outputs.tag }}" - - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - - CHANGELOG_SECTION=$(awk -v version="${{ steps.version.outputs.version }}" ' - /^## \[/ { - if (found) exit - if ($0 ~ "\\[" version "\\]") { - found=1 - next - } - } - found && /^## \[/ { exit } - found { print } - ' CHANGELOG.md || echo "No changelog entry found for this version.") - - echo "$CHANGELOG_SECTION" > release_notes.md - - if gh release view "$TAG_NAME" --repo ${{ github.repository }} >/dev/null 2>&1; then - echo "ℹ️ Release $TAG_NAME already exists, skipping creation" - else - if gh release create "$TAG_NAME" \ - --draft \ - --title "$TAG_NAME" \ - --notes-file release_notes.md \ - --repo ${{ github.repository }}; then - echo "✅ Release $TAG_NAME created successfully" - else - echo "❌ Failed to create release $TAG_NAME" >&2 - rm -f release_notes.md - exit 1 - fi - fi - - rm -f release_notes.md diff --git a/.github/workflows/publish-wheel.yml b/.github/workflows/publish-wheel.yml deleted file mode 100644 index 803d131914..0000000000 --- a/.github/workflows/publish-wheel.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Publish Wheel to PyPI - -on: - workflow_dispatch: - inputs: - version: - description: 'Version of the wheel to publish (format X.Y.Z - no "v"!).' - required: true - -jobs: - publish-wheel: - runs-on: ubuntu-latest - steps: - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: '3.11' - - - name: Download artifact - uses: actions/download-artifact@v7 - with: - name: nemoguardrails-${{ github.event.inputs.version }}.whl - - - name: Publish package - uses: pypa/gh-action-pypi-publish@release/v1 - with: - verbose: true - password: ${{ secrets.PYPI_API_TOKEN }} - packages-dir: ./ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 04950085a4..0000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,142 +0,0 @@ -name: Prepare Release - -on: - workflow_dispatch: - inputs: - version: - description: "New version (e.g. 0.15.1)" - required: true - type: string - bump_type: - description: "Version bump type (only used if version is not provided)" - required: false - type: choice - options: - - patch - - minor - - major - default: patch - -jobs: - release: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: "3.10" - - - name: Bootstrap poetry - run: | - curl -sSL https://install.python-poetry.org | POETRY_VERSION=1.8.2 python - - - - name: Update PATH - run: echo "$HOME/.local/bin" >> $GITHUB_PATH - - - name: Configure poetry - run: poetry config virtualenvs.in-project true - - - name: Install git-cliff - # not allowed by NVIDIA - # uses: kenji-miyake/setup-git-cliff@v2 - run: | - # download and install git-cliff binary directly - CLIFF_VERSION="2.7.0" - wget -q https://github.com/orhun/git-cliff/releases/download/v${CLIFF_VERSION}/git-cliff-${CLIFF_VERSION}-x86_64-unknown-linux-gnu.tar.gz - tar -xzf git-cliff-${CLIFF_VERSION}-x86_64-unknown-linux-gnu.tar.gz - sudo mv git-cliff-${CLIFF_VERSION}/git-cliff /usr/local/bin/ - rm -rf git-cliff-${CLIFF_VERSION}* - # verify installation - git cliff --version - - - name: Determine version - id: version - run: | - if [ -n "${{ github.event.inputs.version }}" ]; then - VERSION="${{ github.event.inputs.version }}" - else - # Use git-cliff to determine the next version based on commits - VERSION=$(git cliff --bumped-version) - fi - - # Remove 'v' prefix if present - VERSION=${VERSION#v} - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "tag=v${VERSION}" >> $GITHUB_OUTPUT - - - name: Generate changelog block - run: | - git cliff \ - --unreleased \ - --tag v${{ steps.version.outputs.version }} \ - --strip all \ - > CHANGELOG.new.md - - - name: Inject release block just above the previous entry - run: | - awk ' - BEGIN { done = 0 } - # On the *first* version header, insert the new block - /^## \[/ && done == 0 { - system("cat CHANGELOG.new.md") - print "" # blank line between blocks - done = 1 - } - { print } - ' CHANGELOG.md > CHANGELOG.tmp \ - && mv CHANGELOG.tmp CHANGELOG.md \ - && rm CHANGELOG.new.md - - - name: Update version with Poetry - run: | - # Use Poetry to update the version - poetry version ${{ steps.version.outputs.version }} - - - name: Update version in README.md - run: | - # Update the version reference in README.md - sed -i "s/\[0\.[0-9]*\.[0-9]*\](https:\/\/github\.com\/NVIDIA\/NeMo-Guardrails\/tree\/v0\.[0-9]*\.[0-9]*)/[${{ steps.version.outputs.version }}](https:\/\/github.com\/NVIDIA\/NeMo-Guardrails\/tree\/v${{ steps.version.outputs.version }})/g" README.md - sed -i "s/version: \[0\.[0-9]*\.[0-9]*\]/version: [${{ steps.version.outputs.version }}]/g" README.md - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: chore/release-${{ steps.version.outputs.version }} - base: develop - title: "chore: prepare for release v${{ steps.version.outputs.version }}" - body: | - ## 🚀 Release v${{ steps.version.outputs.version }} - - This PR was automatically created by the release workflow. - - ### Changes included: - - ✅ Updated version to v${{ steps.version.outputs.version }} - - ✅ Updated CHANGELOG.md with latest changes - - ✅ Updated version references in README.md - - --- - - After merging this PR, a tag will be created and a GitHub release will be published. - labels: | - release - automated - add-paths: | - CHANGELOG.md - pyproject.toml - poetry.lock - README.md - commit-message: "chore(release): prepare for v${{ steps.version.outputs.version }}" - - - name: Clean up - run: rm -f RELEASE_NOTES.md diff --git a/.github/workflows/test-and-build-wheel.yml b/.github/workflows/test-and-build-wheel.yml index 1d884a092a..c981ad6e0a 100644 --- a/.github/workflows/test-and-build-wheel.yml +++ b/.github/workflows/test-and-build-wheel.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest env: POETRY_VERSION: "1.8.2" - PYTHON_VERSION: "3.11" + PYTHON_VERSION: "3.12" outputs: artifact_name: ${{ steps.set-artifact-name.outputs.artifact_name }} steps: @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.12"] steps: - name: Set up Python uses: actions/setup-python@v6 diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 4172725355..ce98dca153 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -10,7 +10,7 @@ on: - "v*" pull_request: paths: - - "Dockerfile" + - "Dockerfile.server" - "pyproject.toml" - "poetry.lock" - ".github/workflows/test-docker.yml" @@ -42,7 +42,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: Dockerfile + file: Dockerfile.server load: true tags: ${{ env.TEST_TAG }} diff --git a/.github/workflows/test-published-dist.yml b/.github/workflows/test-published-dist.yml deleted file mode 100644 index a63f06c36e..0000000000 --- a/.github/workflows/test-published-dist.yml +++ /dev/null @@ -1,53 +0,0 @@ -on: - schedule: - - cron: "0 0 * * *" # 12:00 midnight UTC, daily - - workflow_dispatch: - -jobs: - test-pypi-wheel: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] - steps: - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Wheel from PyPI - run: | - pip install --upgrade pip - pip install "nemoguardrails[all]" --no-cache-dir - - - name: Start server in the background - run: | - nemoguardrails server & - echo "SERVER_PID=$!" >> $GITHUB_ENV - - - name: Wait for server to be up - run: | - echo "Waiting for server to start..." - for i in {1..30}; do - if curl --output /dev/null --silent --head --fail http://localhost:8000; then - echo "Server is up!" - break - else - echo "Waiting..." - sleep 1 - fi - done - - - name: Check server status - run: | - RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/v1/rails/configs) - if [ "$RESPONSE_CODE" -ne 200 ]; then - echo "Server responded with code $RESPONSE_CODE." - exit 1 - fi - - - name: Stop server - if: ${{ success() }} - run: | - kill $SERVER_PID