Skip to content

HAProxy daily maintenance #86

HAProxy daily maintenance

HAProxy daily maintenance #86

name: HAProxy daily maintenance
on:
schedule:
- cron: '0 10 * * *' # Daily at 10:00 UTC
workflow_dispatch:
jobs:
check:
name: Check upstream version and scan current pinned for CVEs
runs-on: ubuntu-22.04
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Get current pinned version
id: current
run: |
version=$(grep 'HAPROXY_VERSION=' ci/build/build-haproxy-dist.sh \
| head -1 \
| sed 's/.*:-\([0-9.]*\)}.*/\1/')
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Current pinned version: $version"
- name: Get latest 2.8.x release from haproxy.org
id: latest
run: |
# HAProxy doesn't tag every patch release on GitHub — only major
# versions. The authoritative source for patch releases is the
# download directory on haproxy.org.
latest=$(curl -fsSL "https://www.haproxy.org/download/2.8/src/" \
| grep -oP 'haproxy-2\.8\.\d+' \
| sort -V \
| uniq \
| tail -1 \
| sed 's/^haproxy-//')
echo "version=$latest" >> "$GITHUB_OUTPUT"
echo "Latest upstream 2.8.x: $latest"
- name: Compare versions
id: compare
run: |
current="${{ steps.current.outputs.version }}"
latest="${{ steps.latest.outputs.version }}"
if [ "$current" != "$latest" ]; then
echo "needs_bump=true" >> "$GITHUB_OUTPUT"
echo "Bump needed: $current → $latest"
else
echo "needs_bump=false" >> "$GITHUB_OUTPUT"
echo "Already up to date: $current"
fi
- name: Check if bump branch already exists
id: branch
if: steps.compare.outputs.needs_bump == 'true'
run: |
latest="${{ steps.latest.outputs.version }}"
branch="bump-haproxy-${latest}"
if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then
echo "Branch $branch already exists on remote — skipping bump"
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Open bump PR
if: steps.compare.outputs.needs_bump == 'true' && steps.branch.outputs.exists == 'false'
run: |
current="${{ steps.current.outputs.version }}"
latest="${{ steps.latest.outputs.version }}"
branch="bump-haproxy-${latest}"
# Compute SHA-256 of the new upstream tarball so the bump PR
# ships a verified checksum (release.yml fails the build otherwise).
tarball_url="https://www.haproxy.org/download/2.8/src/haproxy-${latest}.tar.gz"
new_sha=$(curl -fsSL "$tarball_url" | sha256sum | awk '{print $1}')
echo "New tarball SHA-256: $new_sha"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$branch"
# Update HAPROXY_VERSION default and HAPROXY_SHA256 in build script.
sed -i "s/HAPROXY_VERSION:-${current}/HAPROXY_VERSION:-${latest}/" \
ci/build/build-haproxy-dist.sh
sed -i "s|^HAPROXY_SHA256=.*|HAPROXY_SHA256=\"${new_sha}\"|" \
ci/build/build-haproxy-dist.sh
# Update version in pyproject.toml.
sed -i "s/^version = \"${current}\"/version = \"${latest}\"/" pyproject.toml
git add ci/build/build-haproxy-dist.sh pyproject.toml
git commit -m "Bump HAProxy ${current} → ${latest}"
git push origin "$branch"
gh pr create \
--title "Bump HAProxy ${current} → ${latest}" \
--body "$(cat <<EOF
## Summary
Automated version bump from the daily HAProxy upstream check.
- HAProxy **${current}** → **${latest}**
- Source: ${tarball_url}
- Tarball SHA-256: \`${new_sha}\`
## Checklist
- [ ] Review HAProxy changelog for breaking changes
- [ ] Merging triggers tag-on-bump-merge.yml, which tags \`v${latest}\` and runs release.yml to rebuild and publish the wheel
EOF
)" \
--head "$branch" \
--base main \
--reviewer eicherseiji
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify Slack on version bump
if: steps.compare.outputs.needs_bump == 'true' && steps.branch.outputs.exists == 'false'
run: |
curl -fsSL -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \
-H 'Content-type: application/json' \
-d "{\"text\":\"HAProxy upstream update: ${{ steps.current.outputs.version }} → ${{ steps.latest.outputs.version }}. PR opened in ray-project/ray-haproxy.\"}"
# Daily CVE scan of the currently pinned HAProxy. Independent of whether
# upstream has a new release. Catches CVEs disclosed against the version
# already shipped to Ray Serve users.
- name: Build current pinned HAProxy for scanning
run: |
docker run --rm \
-v "$PWD:/work" \
-e HAPROXY_VERSION=${{ steps.current.outputs.version }} \
-e OUTPUT_DIR=/work/dist \
quay.io/pypa/manylinux2014_x86_64:2026.01.02-1 \
/work/ci/build/build-haproxy-dist.sh
sudo chown -R "$(id -u):$(id -g)" dist/
mkdir -p scan-target
tar -xzf dist/haproxy-linux-x86_64.tar.gz -C scan-target/
- name: Scan current pinned for HIGH/CRITICAL CVEs
id: scan
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh \
| sh -s -- -b /usr/local/bin
grype dir:scan-target/ -o json > scan.json
high_count=$(jq '[.matches[]
| select(.vulnerability.severity == "High" or .vulnerability.severity == "Critical")]
| length' scan.json)
echo "high_count=$high_count" >> "$GITHUB_OUTPUT"
if [ "$high_count" -gt 0 ]; then
summary=$(jq -r '[.matches[]
| select(.vulnerability.severity == "High" or .vulnerability.severity == "Critical")
| "• \(.vulnerability.id) (\(.vulnerability.severity)) in \(.artifact.name) \(.artifact.version)"]
| unique | join("\n")' scan.json)
{
echo "summary<<SCAN_EOF"
echo "$summary"
echo "SCAN_EOF"
} >> "$GITHUB_OUTPUT"
fi
- name: Notify Slack on CVE in current pinned
if: steps.scan.outputs.high_count != '0' && steps.scan.outputs.high_count != ''
run: |
payload=$(jq -n \
--arg ver "${{ steps.current.outputs.version }}" \
--arg n "${{ steps.scan.outputs.high_count }}" \
--arg list "${{ steps.scan.outputs.summary }}" \
'{text: ":rotating_light: Current pinned HAProxy \($ver) has \($n) HIGH/CRITICAL CVE(s):\n\($list)\nTime to update Ray."}')
curl -fsSL -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \
-H 'Content-type: application/json' \
-d "$payload"