Implements weekly upstream rebase cadence automation: - GitLab CI scheduled pipeline (every 6 hours) - Automatic GitHub release detection - JIRA ticket creation via API - Spec file updates with changelog - Automated MR creation with OSCI Bot integration - TEST_MODE support for safe testing Scripts: - check-upstream-release.sh: Poll GitHub API for new releases - create-release-mr.sh: Main automation orchestrator - update-spec.py: Safe spec file updater - create-jira.sh: JIRA API client - test-workflow.sh: Local testing utility Documentation: - scripts/README.md: Technical documentation - AUTOMATION-SETUP.md: Step-by-step setup guide
235 lines
6.5 KiB
Bash
Executable File
235 lines
6.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Main automation script for creating CentOS Stream bootc release MRs
|
|
# Expects VERSION and RELEASE_TAG from release.env
|
|
|
|
set -euo pipefail
|
|
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
readonly SPEC_FILE="$REPO_ROOT/bootc.spec"
|
|
readonly GITHUB_REPO="containers/bootc"
|
|
readonly BASE_BRANCH="${BASE_BRANCH:-c10s}"
|
|
readonly GITLAB_PROJECT_ID="redhat/centos-stream/rpms/bootc"
|
|
readonly TEST_MODE="${TEST_MODE:-false}"
|
|
|
|
log_info() {
|
|
echo "[INFO] $*"
|
|
}
|
|
|
|
log_warn() {
|
|
echo "[WARN] $*" >&2
|
|
}
|
|
|
|
log_error() {
|
|
echo "[ERROR] $*" >&2
|
|
}
|
|
|
|
die() {
|
|
log_error "$*"
|
|
exit 1
|
|
}
|
|
|
|
# Source release.env from check stage
|
|
if [[ -f "$REPO_ROOT/release.env" ]]; then
|
|
source "$REPO_ROOT/release.env"
|
|
else
|
|
die "release.env not found"
|
|
fi
|
|
|
|
[[ -z "${VERSION:-}" ]] && die "VERSION not set in release.env"
|
|
[[ -z "${RELEASE_TAG:-}" ]] && die "RELEASE_TAG not set in release.env"
|
|
|
|
log_info "Starting release automation for bootc ${VERSION}"
|
|
log_info "Release tag: ${RELEASE_TAG}"
|
|
|
|
if [[ "$TEST_MODE" == "true" ]]; then
|
|
log_warn "⚠️ TEST MODE ENABLED - Will stop before making actual changes"
|
|
fi
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
# Step 1: Download tarballs from GitHub
|
|
log_info "Downloading tarballs from GitHub..."
|
|
TARBALL_MAIN="bootc-${VERSION}.tar.zstd"
|
|
TARBALL_VENDOR="bootc-${VERSION}-vendor.tar.zstd"
|
|
DOWNLOAD_URL="https://github.com/${GITHUB_REPO}/releases/download/${RELEASE_TAG}"
|
|
|
|
mkdir -p /tmp/bootc-release
|
|
cd /tmp/bootc-release
|
|
|
|
if ! curl -L -f -o "$TARBALL_MAIN" "${DOWNLOAD_URL}/${TARBALL_MAIN}"; then
|
|
die "Failed to download $TARBALL_MAIN"
|
|
fi
|
|
|
|
if ! curl -L -f -o "$TARBALL_VENDOR" "${DOWNLOAD_URL}/${TARBALL_VENDOR}"; then
|
|
die "Failed to download $TARBALL_VENDOR"
|
|
fi
|
|
|
|
log_info "Downloaded tarballs successfully"
|
|
ls -lh "$TARBALL_MAIN" "$TARBALL_VENDOR"
|
|
|
|
# Step 2: Create JIRA issue
|
|
log_info "Creating JIRA issue..."
|
|
JIRA_TICKET=""
|
|
if JIRA_TICKET=$("$SCRIPT_DIR/create-jira.sh" "$VERSION" "${DOWNLOAD_URL}"); then
|
|
log_info "Created JIRA ticket: $JIRA_TICKET"
|
|
else
|
|
log_warn "Failed to create JIRA ticket (will use placeholder)"
|
|
JIRA_TICKET="RHEL-XXXXX"
|
|
fi
|
|
|
|
# Step 3: Set up git repository
|
|
cd "$REPO_ROOT"
|
|
log_info "Setting up git repository..."
|
|
|
|
# Fetch latest
|
|
git fetch origin "$BASE_BRANCH"
|
|
git checkout "$BASE_BRANCH"
|
|
git pull origin "$BASE_BRANCH"
|
|
|
|
# Create working branch
|
|
WORK_BRANCH="rebase-${VERSION}"
|
|
log_info "Creating branch: $WORK_BRANCH"
|
|
|
|
if git show-ref --verify --quiet "refs/heads/$WORK_BRANCH"; then
|
|
log_warn "Branch $WORK_BRANCH already exists, deleting..."
|
|
git branch -D "$WORK_BRANCH"
|
|
fi
|
|
|
|
git checkout -b "$WORK_BRANCH"
|
|
|
|
# Step 4: Upload sources to lookaside cache
|
|
if [[ "$TEST_MODE" == "true" ]]; then
|
|
log_warn "[TEST MODE] Skipping centpkg upload"
|
|
else
|
|
log_info "Uploading sources to lookaside cache..."
|
|
if ! centpkg new-sources "/tmp/bootc-release/$TARBALL_MAIN" "/tmp/bootc-release/$TARBALL_VENDOR"; then
|
|
die "Failed to upload sources"
|
|
fi
|
|
log_info "Sources uploaded successfully"
|
|
git diff sources .gitignore
|
|
fi
|
|
|
|
# Step 5: Update spec file
|
|
log_info "Updating spec file..."
|
|
if ! "$SCRIPT_DIR/update-spec.py" "$SPEC_FILE" "$VERSION" "$JIRA_TICKET"; then
|
|
die "Failed to update spec file"
|
|
fi
|
|
|
|
# Step 6: Verify spec file
|
|
log_info "Verifying spec file syntax..."
|
|
if ! rpmspec --parse "$SPEC_FILE" > /dev/null 2>&1; then
|
|
die "Spec file has syntax errors"
|
|
fi
|
|
|
|
# Step 7: Commit changes
|
|
if [[ "$TEST_MODE" == "true" ]]; then
|
|
log_warn "[TEST MODE] Would commit changes with message:"
|
|
echo "---"
|
|
echo "spec: new upstream version ${VERSION}"
|
|
if [[ "$JIRA_TICKET" != "RHEL-XXXXX" ]]; then
|
|
echo ""
|
|
echo "Resolves: ${JIRA_TICKET}"
|
|
fi
|
|
echo "---"
|
|
log_info "Changes that would be committed:"
|
|
git diff "$SPEC_FILE"
|
|
log_warn "[TEST MODE] Stopping here - no actual git push or MR creation"
|
|
log_info "Test completed successfully! 🎉"
|
|
exit 0
|
|
fi
|
|
|
|
log_info "Committing changes..."
|
|
git add sources .gitignore "$SPEC_FILE"
|
|
|
|
COMMIT_MSG="spec: new upstream version ${VERSION}"
|
|
if [[ "$JIRA_TICKET" != "RHEL-XXXXX" ]]; then
|
|
COMMIT_MSG="${COMMIT_MSG}
|
|
|
|
Resolves: ${JIRA_TICKET}"
|
|
fi
|
|
|
|
git commit -m "$COMMIT_MSG"
|
|
|
|
log_info "Commit created:"
|
|
git log -1 --oneline
|
|
|
|
# Step 8: Push branch
|
|
log_info "Pushing branch to origin..."
|
|
if ! git push origin "$WORK_BRANCH"; then
|
|
die "Failed to push branch"
|
|
fi
|
|
|
|
log_info "Branch pushed successfully"
|
|
|
|
# Step 9: Create GitLab MR
|
|
log_info "Creating GitLab Merge Request..."
|
|
|
|
MR_TITLE="spec: new upstream version ${VERSION}"
|
|
MR_DESCRIPTION="Automated release update from upstream bootc ${RELEASE_TAG}
|
|
|
|
**Changes:**
|
|
- Update Version to ${VERSION}
|
|
- Reset Release to 1%{?dist}
|
|
- Add changelog entry
|
|
|
|
**Upstream release:** ${DOWNLOAD_URL}
|
|
**JIRA:** ${JIRA_TICKET}
|
|
|
|
---
|
|
_This MR was created automatically by bootc-release-bot._"
|
|
|
|
# URL-encode the project ID
|
|
PROJECT_ID_ENCODED=$(echo "$GITLAB_PROJECT_ID" | sed 's|/|%2F|g')
|
|
|
|
MR_PAYLOAD=$(cat <<EOF
|
|
{
|
|
"source_branch": "$WORK_BRANCH",
|
|
"target_branch": "$BASE_BRANCH",
|
|
"title": "$MR_TITLE",
|
|
"description": $(echo "$MR_DESCRIPTION" | jq -Rs .),
|
|
"remove_source_branch": true
|
|
}
|
|
EOF
|
|
)
|
|
|
|
# Add assignee if configured
|
|
if [[ -n "${ASSIGNEE_USER_ID:-}" ]]; then
|
|
MR_PAYLOAD=$(echo "$MR_PAYLOAD" | jq --argjson id "$ASSIGNEE_USER_ID" '. + {assignee_ids: [$id]}')
|
|
fi
|
|
|
|
GITLAB_API="https://gitlab.com/api/v4/projects/${PROJECT_ID_ENCODED}/merge_requests"
|
|
|
|
MR_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
|
-X POST \
|
|
-H "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$MR_PAYLOAD" \
|
|
"$GITLAB_API")
|
|
|
|
HTTP_CODE=$(echo "$MR_RESPONSE" | tail -n 1)
|
|
BODY=$(echo "$MR_RESPONSE" | sed '$d')
|
|
|
|
if [[ "$HTTP_CODE" -ge 200 ]] && [[ "$HTTP_CODE" -lt 300 ]]; then
|
|
MR_URL=$(echo "$BODY" | jq -r '.web_url')
|
|
MR_IID=$(echo "$BODY" | jq -r '.iid')
|
|
log_info "✅ Merge Request created successfully!"
|
|
echo
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📦 Release: bootc ${VERSION}"
|
|
echo "🎫 JIRA: ${JIRA_TICKET}"
|
|
echo "🔀 MR !${MR_IID}: ${MR_URL}"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo
|
|
echo "Next: OSCI Bot will run checks and auto-merge if all tests pass."
|
|
else
|
|
log_error "Failed to create MR (HTTP $HTTP_CODE)"
|
|
echo "$BODY" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Cleanup
|
|
rm -rf /tmp/bootc-release
|
|
|
|
log_info "Release automation complete!"
|