mirror of
https://github.com/trycua/computer.git
synced 2026-02-14 01:29:22 -06:00
feat: auto-release on PR merge with required release labels
- Add CI check that requires a release label (release:patch, release:minor, release:major, or no-release) on PRs that change publishable packages - Add workflow that triggers release-bump-version on merge based on label - Cascade dedup prevents double-bumping (e.g., core change won't also separately bump computer/agent since the cascade handles it)
This commit is contained in:
96
.github/workflows/ci-require-release-label.yml
vendored
Normal file
96
.github/workflows/ci-require-release-label.yml
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
name: "CI: Require Release Label"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
check-release-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for release label on publishable changes
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Get changed files in this PR
|
||||
CHANGED_FILES=$(gh pr diff "${{ github.event.pull_request.number }}" \
|
||||
--repo "${{ github.repository }}" --name-only 2>/dev/null || true)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No changed files detected, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Map paths to publishable services
|
||||
declare -A PATH_TO_SERVICE=(
|
||||
["libs/python/cua-cli/"]="pypi/cli"
|
||||
["libs/python/agent/"]="pypi/agent"
|
||||
["libs/python/computer/"]="pypi/computer"
|
||||
["libs/python/core/"]="pypi/core"
|
||||
["libs/python/som/"]="pypi/som"
|
||||
["libs/python/bench-ui/"]="pypi/bench-ui"
|
||||
["libs/cua-bench/"]="pypi/bench"
|
||||
["libs/python/computer-server/"]="pypi/computer-server"
|
||||
["libs/python/mcp-server/"]="pypi/mcp-server"
|
||||
["libs/typescript/cua-cli/"]="npm/cli"
|
||||
["libs/typescript/computer/"]="npm/computer"
|
||||
["libs/typescript/core/"]="npm/core"
|
||||
["libs/typescript/playground/"]="npm/playground"
|
||||
["libs/cuabot/"]="npm/cuabot"
|
||||
["libs/xfce/"]="docker/xfce"
|
||||
["libs/kasm/"]="docker/kasm"
|
||||
["libs/lumier/"]="docker/lumier"
|
||||
["libs/qemu-docker/android/"]="docker/qemu-android"
|
||||
["libs/qemu-docker/linux/"]="docker/qemu-linux"
|
||||
["libs/qemu-docker/windows/"]="docker/qemu-windows"
|
||||
["libs/lume/"]="lume"
|
||||
)
|
||||
|
||||
# Find affected services
|
||||
AFFECTED=""
|
||||
for path_prefix in "${!PATH_TO_SERVICE[@]}"; do
|
||||
if echo "$CHANGED_FILES" | grep -q "^${path_prefix}"; then
|
||||
service="${PATH_TO_SERVICE[$path_prefix]}"
|
||||
AFFECTED="${AFFECTED}${service}\n"
|
||||
fi
|
||||
done
|
||||
|
||||
AFFECTED=$(echo -e "$AFFECTED" | sort -u | sed '/^$/d')
|
||||
|
||||
if [ -z "$AFFECTED" ]; then
|
||||
echo "No publishable packages affected, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Affected packages:"
|
||||
echo "$AFFECTED" | while read -r svc; do echo " - $svc"; done
|
||||
|
||||
# Check for release label
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels.*.name) }}'
|
||||
HAS_RELEASE_LABEL=false
|
||||
|
||||
for label in "release:patch" "release:minor" "release:major" "no-release"; do
|
||||
if echo "$LABELS" | grep -q "\"${label}\""; then
|
||||
HAS_RELEASE_LABEL=true
|
||||
echo "Found label: $label"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$HAS_RELEASE_LABEL" = "false" ]; then
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "ERROR: Missing release label!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "This PR changes the following publishable packages:"
|
||||
echo "$AFFECTED" | while read -r svc; do echo " - $svc"; done
|
||||
echo ""
|
||||
echo "Please add one of these labels to the PR:"
|
||||
echo " release:patch - bump patch version (bug fixes)"
|
||||
echo " release:minor - bump minor version (new features)"
|
||||
echo " release:major - bump major version (breaking changes)"
|
||||
echo " no-release - skip publishing (e.g., tests/docs only)"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
127
.github/workflows/release-on-merge.yml
vendored
Normal file
127
.github/workflows/release-on-merge.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: "CD: Auto Release on Merge"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
auto-release:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Detect packages and trigger release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
REPO="${{ github.repository }}"
|
||||
|
||||
# Check labels
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels.*.name) }}'
|
||||
|
||||
# Determine bump type from label
|
||||
BUMP_TYPE=""
|
||||
if echo "$LABELS" | grep -q '"no-release"'; then
|
||||
echo "no-release label found, skipping."
|
||||
exit 0
|
||||
elif echo "$LABELS" | grep -q '"release:major"'; then
|
||||
BUMP_TYPE="major"
|
||||
elif echo "$LABELS" | grep -q '"release:minor"'; then
|
||||
BUMP_TYPE="minor"
|
||||
elif echo "$LABELS" | grep -q '"release:patch"'; then
|
||||
BUMP_TYPE="patch"
|
||||
else
|
||||
echo "No release label found, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Bump type: $BUMP_TYPE"
|
||||
|
||||
# Get changed files
|
||||
CHANGED_FILES=$(gh pr diff "$PR_NUMBER" --repo "$REPO" --name-only 2>/dev/null || true)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No changed files, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Map paths to services
|
||||
declare -A PATH_TO_SERVICE=(
|
||||
["libs/python/cua-cli/"]="pypi/cli"
|
||||
["libs/python/agent/"]="pypi/agent"
|
||||
["libs/python/computer/"]="pypi/computer"
|
||||
["libs/python/core/"]="pypi/core"
|
||||
["libs/python/som/"]="pypi/som"
|
||||
["libs/python/bench-ui/"]="pypi/bench-ui"
|
||||
["libs/cua-bench/"]="pypi/bench"
|
||||
["libs/python/computer-server/"]="pypi/computer-server"
|
||||
["libs/python/mcp-server/"]="pypi/mcp-server"
|
||||
["libs/typescript/cua-cli/"]="npm/cli"
|
||||
["libs/typescript/computer/"]="npm/computer"
|
||||
["libs/typescript/core/"]="npm/core"
|
||||
["libs/typescript/playground/"]="npm/playground"
|
||||
["libs/cuabot/"]="npm/cuabot"
|
||||
["libs/xfce/"]="docker/xfce"
|
||||
["libs/kasm/"]="docker/kasm"
|
||||
["libs/lumier/"]="docker/lumier"
|
||||
["libs/qemu-docker/android/"]="docker/qemu-android"
|
||||
["libs/qemu-docker/linux/"]="docker/qemu-linux"
|
||||
["libs/qemu-docker/windows/"]="docker/qemu-windows"
|
||||
["libs/lume/"]="lume"
|
||||
)
|
||||
|
||||
# Find affected services
|
||||
declare -A AFFECTED_MAP
|
||||
for path_prefix in "${!PATH_TO_SERVICE[@]}"; do
|
||||
if echo "$CHANGED_FILES" | grep -q "^${path_prefix}"; then
|
||||
service="${PATH_TO_SERVICE[$path_prefix]}"
|
||||
AFFECTED_MAP["$service"]=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#AFFECTED_MAP[@]} -eq 0 ]; then
|
||||
echo "No publishable packages affected, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Affected packages before dedup:"
|
||||
for svc in "${!AFFECTED_MAP[@]}"; do echo " - $svc"; done
|
||||
|
||||
# Cascade dedup: remove services already covered by upstream cascades
|
||||
# pypi/core cascades to pypi/computer and pypi/agent
|
||||
if [[ -n "${AFFECTED_MAP["pypi/core"]}" ]]; then
|
||||
unset AFFECTED_MAP["pypi/computer"]
|
||||
unset AFFECTED_MAP["pypi/agent"]
|
||||
fi
|
||||
# pypi/computer cascades to pypi/agent
|
||||
if [[ -n "${AFFECTED_MAP["pypi/computer"]}" ]]; then
|
||||
unset AFFECTED_MAP["pypi/agent"]
|
||||
fi
|
||||
# pypi/som cascades to pypi/agent
|
||||
if [[ -n "${AFFECTED_MAP["pypi/som"]}" ]]; then
|
||||
unset AFFECTED_MAP["pypi/agent"]
|
||||
fi
|
||||
# npm/core cascades to npm/computer
|
||||
if [[ -n "${AFFECTED_MAP["npm/core"]}" ]]; then
|
||||
unset AFFECTED_MAP["npm/computer"]
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Services to release (after cascade dedup):"
|
||||
for svc in "${!AFFECTED_MAP[@]}"; do echo " - $svc"; done
|
||||
|
||||
# Trigger release-bump-version for each affected service
|
||||
for svc in "${!AFFECTED_MAP[@]}"; do
|
||||
echo ""
|
||||
echo "Triggering release-bump-version for: $svc ($BUMP_TYPE)"
|
||||
gh workflow run release-bump-version.yml \
|
||||
--repo "$REPO" \
|
||||
-f service="$svc" \
|
||||
-f bump_type="$BUMP_TYPE"
|
||||
echo "Triggered successfully."
|
||||
# Sleep between triggers to avoid race conditions on main branch
|
||||
sleep 15
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "All releases triggered!"
|
||||
Reference in New Issue
Block a user