.github/workflows/check_release_deps.yml YAML 115 lines View on github.com → Search inside
1# Validate that a release PR's declared dependencies are actually published on2# PyPI *before* the package itself is released.3#4# WHY: `release(scope): x.y.z` PRs frequently bump intra-monorepo minimum pins5# (e.g. `langchain-core>=1.4.4`). The regular PR test suite deliberately SKIPS6# minimum-version resolution for langchain-core / langchain / langchain-text-splitters7# (see `SKIP_IF_PULL_REQUEST` in `.github/scripts/get_min_versions.py`) because normal8# feature PRs may bump those in lockstep with an as-yet-unpublished sibling release.9#10# For a `release` PR, though, every runtime dependency *should* already be on PyPI 11# that is the convention (release `langchain-core` first, then downstream packages).12# If a pin points at a version that does not exist yet, the published wheel's metadata13# is unresolvable and `pip install <pkg>==x.y.z` breaks for end users. Without this14# workflow, that is only caught at release-trigger time, when `get_min_versions.py`15# resolves the pins against PyPI (its companion change in this PR now exits loudly on16# an unpublished pin instead of emitting `pkg==None`). This workflow adds a second,17# earlier guard: it shifts the same check left onto the release PR, so the author18# finds out before merge rather than when the release job runs.19#20# HOW: resolve each changed package's runtime dependencies against real PyPI with21# `uv pip compile --no-sources`, which ignores the editable `[tool.uv.sources]`22# workspace overrides so intra-monorepo deps resolve from the index exactly as an23# end user's installer would see them. This resolves from package index metadata and24# does not build or run the PR's own project code.2526name: "🚀 Check Release Dependencies"2728on:29  pull_request:30    types: [opened, synchronize, reopened, edited]31    paths:32      - "libs/**/pyproject.toml"3334permissions:35  contents: read3637jobs:38  check-release-deps:39    name: "✅ Verify release dependencies exist on PyPI"40    # Only run for release PRs (`release(scope): x.y.z`). Other PRs may bump41    # intra-monorepo pins ahead of a sibling release on purpose.42    if: startsWith(github.event.pull_request.title, 'release')43    runs-on: ubuntu-latest44    timeout-minutes: 1045    steps:46      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v647        with:48          fetch-depth: 04950      - name: "🐍 Set up Python + uv"51        uses: "./.github/actions/uv_setup"52        with:53          python-version: "3.12"54          enable-cache: "false"5556      - name: "🔍 Resolve runtime dependencies against PyPI"57        shell: bash58        env:59          # Passed via env (not inline interpolation) to keep PR-controlled60          # values out of the shell command string.61          BASE_SHA: ${{ github.event.pull_request.base.sha }}62          HEAD_SHA: ${{ github.event.pull_request.head.sha }}63        run: |64          set -euo pipefail6566          # pyproject.toml manifests changed by this PR.67          mapfile -t changed < <(68            git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'libs/**/pyproject.toml'69          )7071          if [ "${#changed[@]}" -eq 0 ]; then72            # The `paths:` filter should prevent this, so an empty list is73            # surprising  surface it loudly rather than passing silently.74            echo "::notice::No libs/**/pyproject.toml changed in this PR; nothing to validate."75            exit 076          fi7778          failed=079          for manifest in "${changed[@]}"; do80            pkg_dir="$(dirname "$manifest")"81            echo "::group::Resolving ${manifest} against PyPI"82            # --no-sources ignores [tool.uv.sources] editable workspace overrides,83            # so intra-monorepo deps resolve from PyPI like an end-user install.84            # --universal resolves across the full requires-python range, so deps85            # gated behind Python-version markers are validated too.86            if uv pip compile --no-sources --universal "$manifest" > /dev/null; then87              echo "✅ ${pkg_dir}: all runtime dependencies resolve on PyPI"88            else89              echo "❌ ${pkg_dir}: a dependency pin is not satisfiable on PyPI"90              failed=191            fi92            echo "::endgroup::"93          done9495          if [ "$failed" -ne 0 ]; then96            cat >&2 <<'EOF'9798          ┌──────────────────────────────────────────────────────────────────┐99           One or more dependency pins could not be resolved from PyPI.       100           See the per-package resolver output above for the exact reason.    101          └──────────────────────────────────────────────────────────────────┘102103          Most likely, a `release(scope): x.y.z` PR pinned a dependency to a104          version that is not yet published on PyPI. The released wheel's metadata105          would then be unresolvable, breaking `pip install` for end users. Fix by:106             Releasing the dependency package first so the pinned version exists107              on PyPI, then re-running this check; or108             Relaxing the version pin to a published version.109110          If the resolver output above shows a network/index error (rather than111          "No solution found"), this may be a transient PyPI issue  re-run the job.112          EOF113            exit 1114          fi

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.