mirror of
https://github.com/hwchase17/langchain.git
synced 2026-06-09 18:50:33 +00:00
Four GitHub Actions workflows ported from the Deep Agents monorepo to enforce repository hygiene rules that were not previously applied here. ## Changes - **Fork-main PR guard**: closes PRs from forks whose head is `main` or `master`, with a sticky comment explaining how to reopen from a feature branch. Prevents the "Update branch" → admin-override path that lets a `Merge branch 'master' into master` commit land on the default branch and bypass squash-only policy. Maintainers can override with a `bypass-fork-main-check` label. - **Monthly uv pin bump**: opens a PR on the first of each month to advance `UV_VERSION` in the composite setup action. Probes `releases.astral.sh` across four architectures before committing so CI doesn't race a lagging mirror on fresh-release days — the gap Dependabot's `github-actions` ecosystem can't cover because it tracks `uses:` SHA pins, not the inline `UV_VERSION` value. - **Extras-sync validation**: a Python script (`check_extras_sync.py`) and companion workflow that detect version-constraint drift between `[project.dependencies]` and `[project.optional-dependencies]` across every `libs/**/pyproject.toml`. Runs on PRs touching any `pyproject.toml` and on pushes to `master`; is a no-op on packages that declare no extras. - **Banned-trailer pre-merge lint**: rejects PR descriptions containing a `Co-authored-by: ... <noreply@anthropic.com>` trailer before the PR reaches merge, where the org ruleset would reject the squash-push anyway. Posts a sticky comment with remediation steps; updates it to a "resolved" state when the trailer is removed, rather than deleting (which requires elevated token scope on fork PRs).
206 lines
8.8 KiB
YAML
206 lines
8.8 KiB
YAML
# Monthly bump of the uv pin in `.github/actions/uv_setup/action.yml`.
|
|
#
|
|
# We pin uv (rather than letting setup-uv resolve latest) because
|
|
# `releases.astral.sh` lags GitHub Releases on new uv versions, causing CI
|
|
# to flap on fresh-release days. This workflow keeps the pin fresh without
|
|
# exposing that race.
|
|
#
|
|
# Dependabot's `github-actions` ecosystem only updates `uses:` SHA pins, not
|
|
# the `UV_VERSION` env value the action passes to `astral-sh/setup-uv`, so we
|
|
# open the PR ourselves. Idempotent: if a PR for the target version already
|
|
# exists, the workflow exits without creating a duplicate.
|
|
|
|
name: "Bump uv pin"
|
|
|
|
on:
|
|
schedule:
|
|
- cron: "0 9 1 * *"
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: bump-uv-pin
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
bump:
|
|
if: github.repository_owner == 'langchain-ai'
|
|
name: "Open PR if uv has a newer release"
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
|
|
- name: Resolve current and latest uv versions
|
|
id: versions
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
action_file=".github/actions/uv_setup/action.yml"
|
|
current=$(grep -oE 'UV_VERSION: "[0-9]+\.[0-9]+\.[0-9]+"' "$action_file" \
|
|
| sed -E 's/UV_VERSION: "([^"]+)"/\1/' | head -n1)
|
|
latest=$(gh api repos/astral-sh/uv/releases/latest --jq .tag_name)
|
|
semver='^[0-9]+\.[0-9]+\.[0-9]+$'
|
|
if [[ ! "$current" =~ $semver ]]; then
|
|
echo "::error::Could not parse current uv pin from $action_file (got '$current')"
|
|
exit 1
|
|
fi
|
|
if [[ ! "$latest" =~ $semver ]]; then
|
|
echo "::error::Unexpected uv tag from GitHub API (got '$latest')"
|
|
exit 1
|
|
fi
|
|
echo "current=$current" >> "$GITHUB_OUTPUT"
|
|
echo "latest=$latest" >> "$GITHUB_OUTPUT"
|
|
echo "branch=chore/bump-uv-$latest" >> "$GITHUB_OUTPUT"
|
|
echo "Current pin: $current"
|
|
echo "Latest uv: $latest"
|
|
|
|
- name: Log if already up to date
|
|
# The actual skip is implemented by the `if:` guards on every
|
|
# subsequent step; this step only emits a log line so the run
|
|
# history shows why no PR was opened.
|
|
if: steps.versions.outputs.current == steps.versions.outputs.latest
|
|
run: echo "uv pin already at ${{ steps.versions.outputs.latest }}; nothing to do."
|
|
|
|
- name: Skip if PR already open for this version
|
|
id: existing
|
|
if: steps.versions.outputs.current != steps.versions.outputs.latest
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
BRANCH: ${{ steps.versions.outputs.branch }}
|
|
run: |
|
|
set -euo pipefail
|
|
count=$(gh pr list --head "$BRANCH" --state open --json number --jq 'length')
|
|
echo "count=$count" >> "$GITHUB_OUTPUT"
|
|
if [ "$count" -gt 0 ]; then
|
|
echo "Open PR already exists for $BRANCH; skipping."
|
|
fi
|
|
|
|
- name: Wait for astral mirror to replicate
|
|
id: mirror
|
|
if: steps.versions.outputs.current != steps.versions.outputs.latest && steps.existing.outputs.count == '0'
|
|
env:
|
|
LATEST: ${{ steps.versions.outputs.latest }}
|
|
run: |
|
|
set -euo pipefail
|
|
# The mirror can lag GitHub Releases. If it hasn't replicated yet,
|
|
# defer the bump rather than landing a pin that races the mirror
|
|
# on every CI run. We probe several arches because partial
|
|
# replication (linux ready, macOS/aarch64 not) would still race
|
|
# CI on other runners.
|
|
assets=(
|
|
"uv-x86_64-unknown-linux-gnu.tar.gz"
|
|
"uv-aarch64-unknown-linux-gnu.tar.gz"
|
|
"uv-x86_64-apple-darwin.tar.gz"
|
|
"uv-aarch64-apple-darwin.tar.gz"
|
|
)
|
|
ready=true
|
|
for asset in "${assets[@]}"; do
|
|
url="https://releases.astral.sh/github/uv/releases/download/${LATEST}/${asset}"
|
|
# `curl -sI` returns nothing on stderr at -s; capture exit code so a
|
|
# permanently broken DNS/TLS path is surfaced instead of collapsing
|
|
# to an opaque "000".
|
|
set +e
|
|
status=$(curl -sIo /dev/null -w '%{http_code}' --max-time 30 "$url" 2>/tmp/curl.err)
|
|
curl_rc=$?
|
|
set -e
|
|
echo "Mirror HEAD $url -> HTTP $status (curl exit=$curl_rc)"
|
|
if [ "$status" != "200" ]; then
|
|
ready=false
|
|
if [ "$curl_rc" -ne 0 ]; then
|
|
echo "::warning::curl failed for $asset (exit=$curl_rc): $(cat /tmp/curl.err 2>/dev/null || true)"
|
|
else
|
|
echo "::warning::astral mirror has not replicated $asset for uv $LATEST yet (HTTP $status)."
|
|
fi
|
|
fi
|
|
done
|
|
if [ "$ready" = "true" ]; then
|
|
echo "ready=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "ready=false" >> "$GITHUB_OUTPUT"
|
|
echo "::warning::Deferring uv bump to $LATEST until all probed arches are mirrored."
|
|
fi
|
|
|
|
- name: Open bump PR
|
|
if: steps.versions.outputs.current != steps.versions.outputs.latest && steps.existing.outputs.count == '0' && steps.mirror.outputs.ready == 'true'
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
CURRENT: ${{ steps.versions.outputs.current }}
|
|
LATEST: ${{ steps.versions.outputs.latest }}
|
|
BRANCH: ${{ steps.versions.outputs.branch }}
|
|
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
|
run: |
|
|
set -euo pipefail
|
|
action_file=".github/actions/uv_setup/action.yml"
|
|
|
|
# `grep -c` returns 1 on no-match and 2 on read errors. We want
|
|
# "no match" surfaced as the explicit count-of-zero check below;
|
|
# read errors must abort. Capture the exit code separately so
|
|
# `set -e` doesn't swallow either case.
|
|
set +e
|
|
before=$(grep -cE "UV_VERSION: \"${CURRENT}\"" "$action_file")
|
|
before_rc=$?
|
|
set -e
|
|
if [ "$before_rc" -gt 1 ]; then
|
|
echo "::error::grep read error on $action_file (exit=$before_rc)"
|
|
exit 1
|
|
fi
|
|
if [ "$before" -ne 1 ]; then
|
|
echo "::error::Expected exactly 1 'UV_VERSION: \"$CURRENT\"' in $action_file, found $before"
|
|
exit 1
|
|
fi
|
|
sed -i -E "s/UV_VERSION: \"${CURRENT}\"/UV_VERSION: \"${LATEST}\"/" "$action_file"
|
|
set +e
|
|
after=$(grep -cE "UV_VERSION: \"${LATEST}\"" "$action_file")
|
|
after_rc=$?
|
|
set -e
|
|
if [ "$after_rc" -gt 1 ]; then
|
|
echo "::error::grep read error on $action_file (exit=$after_rc)"
|
|
exit 1
|
|
fi
|
|
if [ "$after" -ne 1 ]; then
|
|
echo "::error::Expected exactly 1 'UV_VERSION: \"$LATEST\"' after sed, found $after"
|
|
exit 1
|
|
fi
|
|
if git diff --quiet "$action_file"; then
|
|
echo "No changes after sed; bailing out (current=$CURRENT, latest=$LATEST)."
|
|
exit 1
|
|
fi
|
|
|
|
# Reuse-or-recreate orphan branch from a prior run that pushed
|
|
# but failed before `gh pr create` (no open PR sits on it).
|
|
# The delete can race a concurrent run (manual workflow_dispatch
|
|
# firing while the cron is mid-flight, since concurrency group
|
|
# does not cancel-in-progress); fall through with a warning so a
|
|
# losing race does not kill an otherwise-clean job mid-state.
|
|
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
|
|
echo "::warning::Branch $BRANCH exists on origin without an open PR; deleting before recreating."
|
|
if ! git push origin --delete "$BRANCH"; then
|
|
echo "::warning::Delete of $BRANCH failed (concurrent run, or branch already gone); the subsequent push will surface any real conflict."
|
|
fi
|
|
fi
|
|
|
|
git config --local user.name "github-actions[bot]"
|
|
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
git checkout -b "$BRANCH"
|
|
git add "$action_file"
|
|
git commit -m "chore(deps): bump uv to $LATEST"
|
|
git push --set-upstream origin "$BRANCH"
|
|
|
|
body_file="$(mktemp)"
|
|
{
|
|
printf 'Bumps the uv pin in `.github/actions/uv_setup/action.yml` from `%s` to [`%s`](https://github.com/astral-sh/uv/releases/tag/%s).\n\n' "$CURRENT" "$LATEST" "$LATEST"
|
|
printf 'Opened automatically by `bump_uv_pin.yml`. Mirror availability on `releases.astral.sh` was verified before this PR was created, so CI should not race the fallback.\n'
|
|
} > "$body_file"
|
|
|
|
gh pr create \
|
|
--head "$BRANCH" \
|
|
--base "$DEFAULT_BRANCH" \
|
|
--title "chore(deps): bump uv to $LATEST" \
|
|
--body-file "$body_file"
|