| 1 | #!/bin/bash |
| 2 | # |
| 3 | # trigger-ci-build.sh |
| 4 | # Trigger a Buildkite CI build using the bk CLI for the current commit and branch |
| 5 | # with RUN_ALL=1 and NIGHTLY=1 environment variables. |
| 6 | # |
| 7 | # Usage: ./trigger-ci-build.sh [options] |
| 8 | # |
| 9 | # Requires: bk CLI (https://buildkite.com/docs/platform/cli) |
| 10 | # |
| 11 | # SAFETY: Dry-run by default. Use --execute to actually trigger a build. |
| 12 | # |
| 13 | |
| 14 | set -euo pipefail |
| 15 | |
| 16 | # Colors for output |
| 17 | RED='\033[0;31m' |
| 18 | GREEN='\033[0;32m' |
| 19 | YELLOW='\033[1;33m' |
| 20 | BLUE='\033[0;34m' |
| 21 | NC='\033[0m' # No Color |
| 22 | |
| 23 | # Default configuration |
| 24 | PIPELINE="ci" |
| 25 | DRY_RUN=true |
| 26 | TORCH_NIGHTLY=false |
| 27 | |
| 28 | usage() { |
| 29 | cat <<EOF |
| 30 | Usage: $(basename "$0") [options] |
| 31 | |
| 32 | Trigger a Buildkite CI build using the bk CLI for the current commit and branch. |
| 33 | Sets RUN_ALL=1 and NIGHTLY=1 environment variables. |
| 34 | |
| 35 | SAFETY: Dry-run by default. Use --execute to actually trigger a build. |
| 36 | |
| 37 | Options: |
| 38 | --execute Actually trigger the build (default: dry-run) |
| 39 | --pipeline Buildkite pipeline slug (default: ${PIPELINE}) |
| 40 | --commit Override commit SHA (default: current HEAD) |
| 41 | --branch Override branch name (default: current branch) |
| 42 | --message Custom build message (default: auto-generated) |
| 43 | --torch-nightly Also build and run the full suite against torch nightly |
| 44 | (sets TORCH_NIGHTLY=1) |
| 45 | --help Show this help message |
| 46 | |
| 47 | Prerequisites: |
| 48 | - bk CLI installed: brew tap buildkite/buildkite && brew install buildkite/buildkite/bk |
| 49 | - bk configured: bk configure |
| 50 | |
| 51 | Examples: |
| 52 | $(basename "$0") # Dry-run, show what would happen |
| 53 | $(basename "$0") --execute # Actually trigger the build |
| 54 | $(basename "$0") --pipeline ci-shadow # Dry-run with different pipeline |
| 55 | $(basename "$0") --torch-nightly # Dry-run a full torch-nightly run |
| 56 | EOF |
| 57 | exit 1 |
| 58 | } |
| 59 | |
| 60 | log_info() { |
| 61 | echo -e "${BLUE}[INFO]${NC} $1" |
| 62 | } |
| 63 | |
| 64 | log_success() { |
| 65 | echo -e "${GREEN}[OK]${NC} $1" |
| 66 | } |
| 67 | |
| 68 | log_warn() { |
| 69 | echo -e "${YELLOW}[WARN]${NC} $1" |
| 70 | } |
| 71 | |
| 72 | log_error() { |
| 73 | echo -e "${RED}[ERROR]${NC} $1" >&2 |
| 74 | } |
| 75 | |
| 76 | # Parse arguments |
| 77 | COMMIT="" |
| 78 | BRANCH="" |
| 79 | MESSAGE="" |
| 80 | |
| 81 | while [[ $# -gt 0 ]]; do |
| 82 | case $1 in |
| 83 | --execute) |
| 84 | DRY_RUN=false |
| 85 | shift |
| 86 | ;; |
| 87 | --pipeline) |
| 88 | PIPELINE="$2" |
| 89 | shift 2 |
| 90 | ;; |
| 91 | --commit) |
| 92 | COMMIT="$2" |
| 93 | shift 2 |
| 94 | ;; |
| 95 | --branch) |
| 96 | BRANCH="$2" |
| 97 | shift 2 |
| 98 | ;; |
| 99 | --message) |
| 100 | MESSAGE="$2" |
| 101 | shift 2 |
| 102 | ;; |
| 103 | --torch-nightly) |
| 104 | TORCH_NIGHTLY=true |
| 105 | shift |
| 106 | ;; |
| 107 | --help|-h) |
| 108 | usage |
| 109 | ;; |
| 110 | -*) |
| 111 | log_error "Unknown option: $1" |
| 112 | usage |
| 113 | ;; |
| 114 | *) |
| 115 | log_error "Unexpected argument: $1" |
| 116 | usage |
| 117 | ;; |
| 118 | esac |
| 119 | done |
| 120 | |
| 121 | # Check if bk CLI is installed |
| 122 | if ! command -v bk &>/dev/null; then |
| 123 | log_error "Buildkite CLI (bk) is not installed" |
| 124 | echo "" |
| 125 | echo "Install with:" |
| 126 | echo " brew tap buildkite/buildkite && brew install buildkite/buildkite/bk" |
| 127 | echo "" |
| 128 | echo "Then configure:" |
| 129 | echo " bk configure" |
| 130 | exit 1 |
| 131 | fi |
| 132 | |
| 133 | # Check if we're in a git repository |
| 134 | if ! git rev-parse --is-inside-work-tree &>/dev/null; then |
| 135 | log_error "Not in a git repository" |
| 136 | exit 1 |
| 137 | fi |
| 138 | |
| 139 | # Get current commit and branch if not overridden |
| 140 | if [[ -z "$COMMIT" ]]; then |
| 141 | COMMIT=$(git rev-parse HEAD) |
| 142 | fi |
| 143 | |
| 144 | if [[ -z "$BRANCH" ]]; then |
| 145 | BRANCH=$(git branch --show-current) |
| 146 | if [[ -z "$BRANCH" ]]; then |
| 147 | # Detached HEAD state - try to get branch from ref |
| 148 | BRANCH=$(git rev-parse --abbrev-ref HEAD) |
| 149 | fi |
| 150 | fi |
| 151 | |
| 152 | # Generate default message if not provided |
| 153 | if [[ -z "$MESSAGE" ]]; then |
| 154 | COMMIT_MSG=$(git log -1 --pretty=format:"%s" "$COMMIT" 2>/dev/null || echo "Manual build") |
| 155 | MESSAGE="[Manual] ${COMMIT_MSG}" |
| 156 | fi |
| 157 | |
| 158 | # Safety check: Verify the commit exists on the remote |
| 159 | log_info "Verifying commit exists on remote..." |
| 160 | git fetch origin --quiet 2>/dev/null || true |
| 161 | |
| 162 | # Check if commit is reachable from any remote branch |
| 163 | REMOTE_BRANCHES=$(git branch -r --contains "$COMMIT" 2>/dev/null || true) |
| 164 | if [[ -z "$REMOTE_BRANCHES" ]]; then |
| 165 | log_error "Commit ${COMMIT} does not exist on any remote branch!" |
| 166 | echo "" |
| 167 | echo "The CI system will fail to checkout this commit." |
| 168 | echo "Please push your changes first:" |
| 169 | echo "" |
| 170 | echo " git push origin ${BRANCH}" |
| 171 | echo "" |
| 172 | exit 1 |
| 173 | fi |
| 174 | |
| 175 | log_success "Commit found on remote branches:" |
| 176 | echo "$REMOTE_BRANCHES" | head -5 | sed 's/^/ /' |
| 177 | if [[ $(echo "$REMOTE_BRANCHES" | wc -l) -gt 5 ]]; then |
| 178 | echo " ... and more" |
| 179 | fi |
| 180 | echo "" |
| 181 | |
| 182 | # Environment variables passed to the build. |
| 183 | BUILD_ENV=("RUN_ALL=1" "NIGHTLY=1") |
| 184 | if [[ "$TORCH_NIGHTLY" == true ]]; then |
| 185 | BUILD_ENV+=("TORCH_NIGHTLY=1") |
| 186 | fi |
| 187 | |
| 188 | log_info "Pipeline: ${PIPELINE}" |
| 189 | log_info "Branch: ${BRANCH}" |
| 190 | log_info "Commit: ${COMMIT}" |
| 191 | log_info "Message: ${MESSAGE}" |
| 192 | log_info "Environment: ${BUILD_ENV[*]}" |
| 193 | echo "" |
| 194 | |
| 195 | # Build the command |
| 196 | CMD=(bk build create |
| 197 | -y |
| 198 | -w |
| 199 | -i |
| 200 | --pipeline "${PIPELINE}" |
| 201 | --commit "${COMMIT}" |
| 202 | --branch "${BRANCH}" |
| 203 | --message "${MESSAGE}" |
| 204 | ) |
| 205 | for env_var in "${BUILD_ENV[@]}"; do |
| 206 | CMD+=(--env "${env_var}") |
| 207 | done |
| 208 | |
| 209 | if [[ "$DRY_RUN" == true ]]; then |
| 210 | echo "==========================================" |
| 211 | log_warn "DRY-RUN MODE - No build will be triggered" |
| 212 | echo "==========================================" |
| 213 | echo "" |
| 214 | echo "Command that would be executed:" |
| 215 | echo "" |
| 216 | # Escape single quotes in values for safe shell display |
| 217 | escape_for_shell() { |
| 218 | printf '%s' "$1" | sed "s/'/'\\\\''/g" |
| 219 | } |
| 220 | echo " bk build create \\" |
| 221 | echo " -y \\" |
| 222 | echo " -w \\" |
| 223 | echo " -i \\" |
| 224 | echo " --pipeline '$(escape_for_shell "${PIPELINE}")' \\" |
| 225 | echo " --commit '$(escape_for_shell "${COMMIT}")' \\" |
| 226 | echo " --branch '$(escape_for_shell "${BRANCH}")' \\" |
| 227 | echo " --message '$(escape_for_shell "${MESSAGE}")' \\" |
| 228 | last_idx=$(( ${#BUILD_ENV[@]} - 1 )) |
| 229 | for i in "${!BUILD_ENV[@]}"; do |
| 230 | if [[ $i -eq $last_idx ]]; then |
| 231 | echo " --env '$(escape_for_shell "${BUILD_ENV[$i]}")'" |
| 232 | else |
| 233 | echo " --env '$(escape_for_shell "${BUILD_ENV[$i]}")' \\" |
| 234 | fi |
| 235 | done |
| 236 | echo "" |
| 237 | echo "==========================================" |
| 238 | echo -e "${YELLOW}To actually trigger this build, run:${NC}" |
| 239 | echo "" |
| 240 | echo " $0 --execute" |
| 241 | echo "==========================================" |
| 242 | exit 0 |
| 243 | fi |
| 244 | |
| 245 | log_info "Triggering build..." |
| 246 | |
| 247 | # Execute the command - bk will print the URL and open browser |
| 248 | "${CMD[@]}" |