Add automated release pipeline for CentOS Stream

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
This commit is contained in:
gursewak1997 2026-07-06 16:30:22 -07:00
parent 336d60ff76
commit f8bd4576cf
8 changed files with 1140 additions and 0 deletions

53
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,53 @@
---
# GitLab CI pipeline for automated bootc releases to CentOS Stream
# Runs every 6 hours to check for new upstream releases
workflow:
rules:
- when: always
stages:
- check
- release
# Check if there's a new upstream release
check_new_release:
stage: check
image: quay.io/centos/centos:stream10
before_script:
- dnf install -y git curl jq rpmdevtools
script:
- ./scripts/check-upstream-release.sh
artifacts:
reports:
dotenv: release.env
paths:
- release.env
expire_in: 1 hour
# Create MR if new release detected
create_release_mr:
stage: release
image: quay.io/centos/centos:stream10
before_script:
- dnf install -y git curl jq python3 rpmdevtools centpkg
# Configure git for bot
- git config --global user.name "bootc-release-bot"
- git config --global user.email "${BOT_EMAIL:-bootc-release-bot@redhat.com}"
# Set up SSH for centpkg (skip if TEST_MODE)
- |
if [[ "${TEST_MODE:-false}" != "true" ]]; then
mkdir -p ~/.ssh
echo "$CENTOS_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -t rsa gitlab.com >> ~/.ssh/known_hosts
fi
script:
- ./scripts/create-release-mr.sh
rules:
- if: $TEST_MODE == "true"
- if: $NEW_RELEASE == "true"
needs:
- check_new_release
dependencies:
- check_new_release

216
AUTOMATION-SETUP.md Normal file
View File

@ -0,0 +1,216 @@
# bootc CentOS Stream Automation - Setup Guide
This guide covers the steps needed to activate the automated release process.
## ✅ What's Been Created
The following automation infrastructure is ready:
- `.gitlab-ci.yml` - GitLab CI pipeline configuration
- `scripts/check-upstream-release.sh` - GitHub API poller
- `scripts/create-release-mr.sh` - Main automation orchestrator
- `scripts/update-spec.py` - Spec file updater
- `scripts/create-jira.sh` - JIRA API client
- `scripts/README.md` - Complete documentation
## 🧪 Testing the Workflow
Before setting up production automation, you can test locally:
```bash
# Test release detection and spec update (safe, no changes)
./scripts/test-workflow.sh
# Test in GitLab CI with TEST_MODE (stops before git push)
# 1. Push this branch to GitLab
# 2. Go to CI/CD → Pipelines → Run Pipeline
# 3. Add variable: TEST_MODE=true
# 4. Watch the pipeline execute and stop before making changes
```
**Note:** This automation is currently configured for **c10s only**. To enable for c9s:
1. Create a separate branch from c9s: `git checkout c9s && git checkout -b c9s-add-release-automation`
2. Merge or cherry-pick these changes
3. Update `.gitlab-ci.yml` to set `BASE_BRANCH: c9s`
4. Set up a separate scheduled pipeline for c9s
## 🔧 Required Setup Steps
### 1. Bot Account Creation
Create `bootc-release-bot` account (similar to imagebuilder-bot):
**GitLab Account:**
- Email: `bootc-release-bot@redhat.com`
- Add as Developer to: `gitlab.com/redhat/centos-stream/rpms/bootc`
**JIRA Account:**
- Same email: `bootc-release-bot@redhat.com`
- Request permissions to create issues in RHEL project
### 2. Generate Credentials
**GitLab API Token:**
1. Log in as bootc-release-bot
2. Go to User Settings → Access Tokens
3. Create token with scopes:
- `api`
- `write_repository`
4. Save the token securely
**GitLab SSH Key (for centpkg):**
```bash
ssh-keygen -t ed25519 -C "bootc-release-bot@redhat.com" -f ~/.ssh/bootc-bot
# Add public key to GitLab: Settings → SSH Keys
# Save private key for GitLab CI variable
```
**JIRA API Token:**
1. Log in to https://issues.redhat.com as bootc-release-bot
2. Go to Account Settings → Security → API Tokens
3. Create new token
4. Save the token securely
### 3. Configure GitLab CI/CD Variables
In the bootc GitLab project, go to **Settings → CI/CD → Variables** and add:
| Variable Name | Value | Type | Masked | Protected |
|---------------|-------|------|--------|-----------|
| `JIRA_API_TOKEN` | (JIRA token from step 2) | Variable | ✅ | ❌ |
| `GITLAB_API_TOKEN` | (GitLab token from step 2) | Variable | ✅ | ❌ |
| `CENTOS_SSH_PRIVATE_KEY` | (SSH private key from step 2) | File | ✅ | ❌ |
| `BOT_EMAIL` | `bootc-release-bot@redhat.com` | Variable | ❌ | ❌ |
| `ASSIGNEE_USER_ID` | (Your GitLab user ID) | Variable | ❌ | ❌ |
**To find your GitLab user ID:**
```bash
# Using GitLab API
curl "https://gitlab.com/api/v4/users?username=YOUR_USERNAME" | jq '.[0].id'
# Or check your profile URL: gitlab.com/YOUR_USERNAME
# The numeric ID is shown in the API response
```
### 4. Set Up Scheduled Pipeline
1. Go to **Settings → CI/CD → Schedules**
2. Click **New schedule**
3. Fill in:
- **Description:** `Check for new bootc releases`
- **Interval Pattern:** Custom
- **Cron:** `0 */6 * * *` (every 6 hours)
- **Cron timezone:** UTC
- **Target branch:** `c10s`
- **Active:**
4. Save schedule
### 5. Commit Automation to Repository
```bash
git add .gitlab-ci.yml scripts/
git commit -m "Add automated release pipeline
Implements weekly upstream rebase cadence automation:
- Polls GitHub every 6 hours for new releases
- Creates JIRA tickets automatically
- Updates spec file and creates MRs
- Leverages OSCI Bot for auto-merge
Resolves: #RHEL-XXXXX"
git push origin c9s
```
### 6. Test the Automation
**Dry Run Test:**
1. Go to **CI/CD → Pipelines**
2. Click **Run pipeline**
3. Select branch: `c10s`
4. Click **Run pipeline**
5. Watch the pipeline execute
6. Verify it detects the current version correctly
**Live Test (when next release happens):**
1. Wait for next upstream bootc release
2. Within 6 hours, check for automated MR
3. Verify MR has:
- Correct version in title and spec
- JIRA ticket created and referenced
- Assignee set for notifications
4. Monitor OSCI Bot checks
5. Confirm auto-merge if tests pass
## 📋 Checklist
Before going live, ensure:
- [ ] `bootc-release-bot` account created
- [ ] Bot added as Developer to GitLab project
- [ ] Bot has JIRA issue creation permissions
- [ ] GitLab API token generated
- [ ] SSH key generated and added
- [ ] JIRA API token generated
- [ ] All 5 CI/CD variables configured
- [ ] Scheduled pipeline created
- [ ] Automation code committed to `c9s`
- [ ] Dry run pipeline executed successfully
- [ ] Team notified about new automation
## 🎯 Expected Behavior
Once active:
1. **Every 6 hours:** Pipeline checks for new releases
2. **When new release found:**
- JIRA ticket created automatically
- Tarballs downloaded from GitHub
- Spec file updated with changelog
- MR created and assigned
3. **OSCI Bot workflow:**
- Runs all required tests
- Auto-approves if tests pass
- Auto-merges with Red Hat approval
4. **If tests fail:**
- Assignee gets email notification
- Manual intervention required
## 🆘 Rollback Plan
If automation causes issues:
1. **Disable schedule:**
- Go to Settings → CI/CD → Schedules
- Toggle schedule to inactive
2. **Revert code:**
```bash
git revert <commit-hash>
git push origin c9s
```
3. **Return to manual process** (documented in scripts/README.md)
## 📞 Support Contacts
- GitLab permissions: (Your GitLab admin contact)
- JIRA permissions: (Your JIRA admin contact)
- Bot account setup: (Your IT/identity management contact)
## 🔍 Monitoring
Track automation health:
- **Pipeline success rate:** Settings → CI/CD → Pipelines
- **MR auto-merge rate:** Check recent MRs for OSCI Bot approvals
- **Missed releases:** Compare GitHub releases vs CentOS Stream versions weekly
Target metrics:
- Pipeline success rate: >95%
- Auto-merge rate: >90%
- Release detection latency: <6 hours
---
**Questions or issues?** See `scripts/README.md` for troubleshooting guide.

242
scripts/README.md Normal file
View File

@ -0,0 +1,242 @@
# bootc CentOS Stream Release Automation
This directory contains scripts for automating bootc releases to CentOS Stream.
## Overview
The automation runs as a GitLab CI scheduled pipeline (every 6 hours) that:
1. Checks GitHub for new bootc releases
2. Creates a JIRA ticket for tracking
3. Downloads tarballs from GitHub
4. Updates the spec file and sources
5. Creates a GitLab Merge Request
6. OSCI Bot handles CI checks and auto-merges if all tests pass
## Architecture
```
┌─────────────────────────────────────────────┐
│ GitLab Scheduled Pipeline (every 6 hours) │
└──────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────┐
│ check_new_release job │
│ → scripts/check-upstream-release.sh │
│ → Compares GitHub latest vs spec version │
│ → Outputs: release.env │
└──────────────┬──────────────────────────────┘
│ if NEW_RELEASE=true
┌─────────────────────────────────────────────┐
│ create_release_mr job │
│ → scripts/create-release-mr.sh │
│ ├─ Download tarballs from GitHub │
│ ├─ Create JIRA (scripts/create-jira.sh) │
│ ├─ Upload sources via centpkg │
│ ├─ Update spec (scripts/update-spec.py) │
│ ├─ Commit and push branch │
│ └─ Create GitLab MR via API │
└──────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────┐
│ OSCI Bot Workflow (automatic) │
│ → Runs required checks │
│ → Auto-approves if tests pass │
│ → Auto-merges with Red Hat approval │
└─────────────────────────────────────────────┘
```
## Scripts
### `check-upstream-release.sh`
Polls the GitHub API for the latest bootc release and compares it with the current spec file version.
**Usage:**
```bash
./scripts/check-upstream-release.sh
```
**Output:**
Creates `release.env` with:
- `NEW_RELEASE` (true/false)
- `VERSION` (e.g., "1.5.2")
- `RELEASE_TAG` (e.g., "v1.5.2")
- `CURRENT_VERSION` (current spec version)
### `create-release-mr.sh`
Main automation script that orchestrates the entire release process.
**Prerequisites:**
- `release.env` must exist (created by check-upstream-release.sh)
- Environment variables configured (see below)
- `centpkg` installed and configured
- Git credentials set up
**What it does:**
1. Downloads tarballs from GitHub releases
2. Creates JIRA ticket via API
3. Creates working branch (`rebase-X.Y.Z`)
4. Uploads sources to CentOS lookaside cache
5. Updates spec file (Version, Release, changelog)
6. Commits and pushes changes
7. Creates GitLab MR with assignee
**Usage:**
```bash
# Normally run by GitLab CI, but can be run manually:
export VERSION=1.5.2
export RELEASE_TAG=v1.5.2
./scripts/create-release-mr.sh
```
### `update-spec.py`
Python helper to safely update the spec file.
**Usage:**
```bash
./scripts/update-spec.py <spec_file> <version> <jira_ticket> [author]
```
**Example:**
```bash
./scripts/update-spec.py bootc.spec 1.5.2 RHEL-104567
```
**What it does:**
- Updates `Version:` field
- Resets `Release:` to `1%{?dist}`
- Adds properly formatted changelog entry
- Preserves spec file formatting
### `create-jira.sh`
Creates a JIRA issue for the release via REST API.
**Usage:**
```bash
export JIRA_API_TOKEN="your-token-here"
./scripts/create-jira.sh <version> [upstream_url]
```
**Example:**
```bash
./scripts/create-jira.sh 1.5.2
# Outputs: RHEL-104567
```
**Returns:** JIRA ticket key (e.g., `RHEL-104567`) on stdout
## GitLab CI/CD Variables
Configure these in **Settings → CI/CD → Variables**:
| Variable | Description | Type |
|----------|-------------|------|
| `JIRA_API_TOKEN` | JIRA API token for bootc-release-bot | Masked |
| `GITLAB_API_TOKEN` | GitLab API token (scope: `api`, `write_repository`) | Masked |
| `CENTOS_SSH_PRIVATE_KEY` | SSH private key for centpkg lookaside uploads | File, Masked |
| `BOT_EMAIL` | bootc-release-bot@redhat.com | Variable |
| `ASSIGNEE_USER_ID` | GitLab user ID for MR assignee (e.g., `1234567`) | Variable |
## GitLab Schedule Setup
1. Go to **Settings → CI/CD → Schedules**
2. Click **New schedule**
3. Configure:
- **Description:** "Check for new bootc releases"
- **Interval:** Custom → `0 */6 * * *` (every 6 hours)
- **Target branch:** `c10s`
- **Active:** ✅ Yes
## Manual Testing
### Test the release checker
```bash
./scripts/check-upstream-release.sh
cat release.env
```
### Test spec file update
```bash
./scripts/update-spec.py bootc.spec 1.5.2 RHEL-TEST-001 "Your Name <your@email.com>"
git diff bootc.spec
git restore bootc.spec # Undo changes
```
### Test JIRA creation (requires API token)
```bash
export JIRA_API_TOKEN="your-token"
./scripts/create-jira.sh 1.5.2
```
### Trigger pipeline manually
1. Go to **CI/CD → Pipelines**
2. Click **Run pipeline**
3. Select branch: `c10s`
4. Add variable: `CI_PIPELINE_SOURCE` = `web`
5. Click **Run pipeline**
## Troubleshooting
### Pipeline fails at check_new_release
- Verify GitHub API is accessible: `curl -s https://api.github.com/repos/containers/bootc/releases/latest | jq .tag_name`
- Check spec file exists and is parseable: `rpmspec -q --queryformat='%{version}\n' bootc.spec`
### Pipeline fails at create_release_mr
- **JIRA creation fails:** Check `JIRA_API_TOKEN` is valid and bot has permissions
- The automation continues with placeholder `RHEL-XXXXX` if JIRA fails
- **centpkg upload fails:** Check `CENTOS_SSH_PRIVATE_KEY` is configured correctly
- **Git push fails:** Verify bot has Developer role on the GitLab project
- **MR creation fails:** Check `GITLAB_API_TOKEN` has `api` and `write_repository` scopes
### MR created but not auto-merging
- Check OSCI Bot comments on the MR for test failures
- Ensure another Red Hatter approves the MR (required for auto-merge)
- Review failed tests and fix issues
### No new releases detected
- Verify upstream has actually released a new version: https://github.com/containers/bootc/releases
- Check current spec version matches latest upstream: `rpmspec -q --queryformat='%{version}\n' bootc.spec`
## Bot Account Setup
The automation uses `bootc-release-bot` account:
**GitLab:**
- Developer role on `gitlab.com/redhat/centos-stream/rpms/bootc`
- API token with `api` and `write_repository` scopes
- SSH key for centpkg uploads
**JIRA:**
- Account with issue creation permissions in RHEL project
- API token configured in GitLab CI/CD variables
**Git config:**
```bash
git config user.name "bootc-release-bot"
git config user.email "bootc-release-bot@redhat.com"
```
## Weekly Cadence
With the 6-hour scheduled pipeline, new releases are detected within:
- **Best case:** <6 hours after upstream release
- **Average:** 3 hours after upstream release
- **Worst case:** ~6 hours after upstream release
This ensures the weekly cadence is maintained with minimal manual intervention.
## Success Metrics
- ✅ Zero missed upstream releases
- ✅ MRs created within 6 hours of upstream release
- ✅ Auto-merge rate >90% (indicates healthy CI)
- ✅ Manual intervention required <10% of releases
## Support
For issues or questions:
- Check GitLab pipeline logs for detailed error messages
- Review OSCI Bot comments on MRs for test failures
- Contact the bootc team for bot account or permission issues

View File

@ -0,0 +1,65 @@
#!/bin/bash
# Check for new bootc releases on GitHub
# Outputs release.env file with NEW_RELEASE, VERSION, and RELEASE_TAG variables
set -euo pipefail
readonly GITHUB_API="https://api.github.com/repos/containers/bootc/releases/latest"
readonly SPEC_FILE="bootc.spec"
log_info() {
echo "[INFO] $*"
}
log_error() {
echo "[ERROR] $*" >&2
}
# Fetch latest release from GitHub
log_info "Fetching latest release from GitHub..."
LATEST_RELEASE=$(curl -sL "$GITHUB_API" | jq -r '.tag_name')
if [[ -z "$LATEST_RELEASE" ]] || [[ "$LATEST_RELEASE" == "null" ]]; then
log_error "Failed to fetch latest release from GitHub"
exit 1
fi
# Strip 'v' prefix from tag (e.g., v1.5.2 -> 1.5.2)
LATEST_VERSION="${LATEST_RELEASE#v}"
log_info "Latest upstream release: $LATEST_VERSION ($LATEST_RELEASE)"
# Get current version from spec file
if [[ ! -f "$SPEC_FILE" ]]; then
log_error "Spec file not found: $SPEC_FILE"
exit 1
fi
CURRENT_VERSION=$(rpmspec -q --queryformat='%{version}\n' "$SPEC_FILE" 2>/dev/null | head -1)
if [[ -z "$CURRENT_VERSION" ]]; then
log_error "Failed to parse version from spec file"
exit 1
fi
log_info "Current spec version: $CURRENT_VERSION"
# Compare versions
if [[ "$LATEST_VERSION" != "$CURRENT_VERSION" ]]; then
log_info "New release detected: $CURRENT_VERSION -> $LATEST_VERSION"
cat > release.env <<EOF
NEW_RELEASE=true
VERSION=$LATEST_VERSION
RELEASE_TAG=$LATEST_RELEASE
CURRENT_VERSION=$CURRENT_VERSION
EOF
else
log_info "No new release (current: $CURRENT_VERSION)"
cat > release.env <<EOF
NEW_RELEASE=false
VERSION=$CURRENT_VERSION
RELEASE_TAG=$LATEST_RELEASE
CURRENT_VERSION=$CURRENT_VERSION
EOF
fi
cat release.env

102
scripts/create-jira.sh Executable file
View File

@ -0,0 +1,102 @@
#!/bin/bash
# Create JIRA issue for bootc release
# Returns JIRA ticket key (e.g., RHEL-104567) on success
set -euo pipefail
VERSION="${1:-}"
UPSTREAM_URL="${2:-}"
if [[ -z "$VERSION" ]]; then
echo "Usage: $0 <version> [upstream_url]" >&2
exit 1
fi
if [[ -z "$UPSTREAM_URL" ]]; then
UPSTREAM_URL="https://github.com/containers/bootc/releases/tag/v${VERSION}"
fi
log_info() {
echo "[JIRA] $*"
}
log_error() {
echo "[JIRA ERROR] $*" >&2
}
# Check for required env vars
if [[ -z "${JIRA_API_TOKEN:-}" ]]; then
log_error "JIRA_API_TOKEN environment variable not set"
exit 1
fi
# JIRA API endpoint (v3 as per Red Hat Jira)
JIRA_API="https://issues.redhat.com/rest/api/3/issue"
# Create payload
SUMMARY="Update bootc to ${VERSION} in CentOS Stream"
DESCRIPTION="Automated release update from upstream bootc v${VERSION}
Upstream release: ${UPSTREAM_URL}
This issue was created automatically by bootc-release-bot."
PAYLOAD=$(cat <<EOF
{
"fields": {
"project": {
"key": "RHEL"
},
"summary": "$SUMMARY",
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "$DESCRIPTION"
}
]
}
]
},
"issuetype": {
"name": "Story"
}
}
}
EOF
)
log_info "Creating JIRA issue: $SUMMARY"
# Make API call
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Authorization: Bearer ${JIRA_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"$JIRA_API")
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [[ "$HTTP_CODE" -ge 200 ]] && [[ "$HTTP_CODE" -lt 300 ]]; then
JIRA_KEY=$(echo "$BODY" | jq -r '.key')
if [[ -n "$JIRA_KEY" ]] && [[ "$JIRA_KEY" != "null" ]]; then
log_info "Successfully created: $JIRA_KEY"
echo "$JIRA_KEY"
exit 0
else
log_error "Failed to parse JIRA key from response"
echo "$BODY" >&2
exit 1
fi
else
log_error "API request failed with HTTP $HTTP_CODE"
echo "$BODY" >&2
exit 1
fi

234
scripts/create-release-mr.sh Executable file
View File

@ -0,0 +1,234 @@
#!/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!"

142
scripts/test-workflow.sh Executable file
View File

@ -0,0 +1,142 @@
#!/bin/bash
# Test the release automation workflow locally (dry-run mode)
# This script simulates what the GitLab CI pipeline does without making actual changes
set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
echo "========================================="
echo "bootc Release Automation - Test Mode"
echo "========================================="
echo
cd "$REPO_ROOT"
# Step 1: Test release checker
echo "📡 Step 1: Checking for new releases..."
echo "----------------------------------------"
if ! "$SCRIPT_DIR/check-upstream-release.sh"; then
echo "❌ Failed to check releases"
exit 1
fi
echo
echo "📄 Release info:"
cat release.env
echo
source release.env
if [[ "$NEW_RELEASE" != "true" ]]; then
echo "✅ No new release detected"
echo "Current version: $CURRENT_VERSION"
echo "Latest upstream: $VERSION"
echo
echo "To test with a fake version, modify check-upstream-release.sh temporarily."
exit 0
fi
echo "🆕 New release detected: $VERSION"
echo
# Step 2: Test JIRA creation (optional, requires token)
echo "🎫 Step 2: Testing JIRA creation (optional)..."
echo "----------------------------------------"
if [[ -n "${JIRA_API_TOKEN:-}" ]]; then
echo "JIRA_API_TOKEN is set, testing creation..."
if JIRA_TICKET=$("$SCRIPT_DIR/create-jira.sh" "$VERSION"); then
echo "✅ JIRA ticket created: $JIRA_TICKET"
echo
echo "⚠️ WARNING: This created a REAL JIRA ticket!"
echo "You may want to close/delete it: https://issues.redhat.com/browse/$JIRA_TICKET"
else
echo "❌ JIRA creation failed (this is OK for testing)"
JIRA_TICKET="RHEL-TEST-001"
fi
else
echo "⏭️ Skipping JIRA creation (JIRA_API_TOKEN not set)"
JIRA_TICKET="RHEL-TEST-001"
fi
echo
# Step 3: Test spec file update (in a temporary copy)
echo "📝 Step 3: Testing spec file update..."
echo "----------------------------------------"
TEMP_SPEC=$(mktemp)
cp bootc.spec "$TEMP_SPEC"
if "$SCRIPT_DIR/update-spec.py" "$TEMP_SPEC" "$VERSION" "$JIRA_TICKET" "Test Bot <test@example.com>"; then
echo "✅ Spec file updated successfully"
echo
echo "Diff preview:"
diff -u bootc.spec "$TEMP_SPEC" || true
echo
else
echo "❌ Failed to update spec file"
rm -f "$TEMP_SPEC"
exit 1
fi
# Verify spec syntax
if rpmspec --parse "$TEMP_SPEC" > /dev/null 2>&1; then
echo "✅ Spec file syntax is valid"
else
echo "❌ Spec file has syntax errors"
rm -f "$TEMP_SPEC"
exit 1
fi
rm -f "$TEMP_SPEC"
echo
# Step 4: Simulate tarball download
echo "📦 Step 4: Checking tarball availability..."
echo "----------------------------------------"
DOWNLOAD_URL="https://github.com/containers/bootc/releases/download/${RELEASE_TAG}"
TARBALL_MAIN="bootc-${VERSION}.tar.zstd"
TARBALL_VENDOR="bootc-${VERSION}-vendor.tar.zstd"
if curl -I -s -f "${DOWNLOAD_URL}/${TARBALL_MAIN}" > /dev/null; then
echo "✅ Main tarball exists: ${TARBALL_MAIN}"
else
echo "❌ Main tarball not found: ${DOWNLOAD_URL}/${TARBALL_MAIN}"
fi
if curl -I -s -f "${DOWNLOAD_URL}/${TARBALL_VENDOR}" > /dev/null; then
echo "✅ Vendor tarball exists: ${TARBALL_VENDOR}"
else
echo "❌ Vendor tarball not found: ${DOWNLOAD_URL}/${TARBALL_VENDOR}"
fi
echo
# Summary
echo "========================================="
echo "✅ Test Complete!"
echo "========================================="
echo
echo "Summary:"
echo " • Release detection: ✅"
echo " • Spec file update: ✅"
echo " • Spec syntax validation: ✅"
echo " • Tarballs available: ✅"
if [[ -n "${JIRA_API_TOKEN:-}" ]]; then
echo " • JIRA creation: ✅ (real ticket created: $JIRA_TICKET)"
else
echo " • JIRA creation: ⏭️ (skipped, no token)"
fi
echo
echo "Next steps to test full workflow:"
echo " 1. Commit this automation to a test branch"
echo " 2. Configure GitLab CI/CD variables (see AUTOMATION-SETUP.md)"
echo " 3. Push the branch to GitLab"
echo " 4. Run pipeline manually: CI/CD → Pipelines → Run Pipeline"
echo " 5. Add variable: TEST_MODE=true for dry-run"
echo
echo "The real workflow will:"
echo " • Download tarballs"
echo " • Upload to lookaside cache with centpkg"
echo " • Create git branch and commit"
echo " • Push to GitLab and create MR"
echo

86
scripts/update-spec.py Executable file
View File

@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""
Update bootc.spec file with new version and changelog entry
"""
import re
import sys
from datetime import datetime
from pathlib import Path
def update_spec(spec_path: str, version: str, jira_ticket: str, author: str = None) -> None:
"""
Update spec file with new version and changelog entry.
Args:
spec_path: Path to the spec file
version: New version number (e.g., "1.5.2")
jira_ticket: JIRA ticket number (e.g., "RHEL-104567")
author: Changelog author (default: bootc-release-bot)
"""
if author is None:
author = "bootc-release-bot <bootc-release-bot@redhat.com>"
spec_file = Path(spec_path)
if not spec_file.exists():
print(f"Error: Spec file not found: {spec_path}", file=sys.stderr)
sys.exit(1)
content = spec_file.read_text()
# Update Version field (preserve spacing)
content = re.sub(
r'^(Version:\s+).*',
rf'\g<1>{version}',
content,
flags=re.MULTILINE
)
# Update Release field to 1%{?dist}
content = re.sub(
r'^(Release:\s+).*',
r'\g<1>1%{?dist}',
content,
flags=re.MULTILINE
)
# Generate changelog entry
date_str = datetime.now().strftime('%a %b %d %Y')
changelog_entry = f"""* {date_str} {author} - {version}-1
- Update to {version}
- Resolves: #{jira_ticket}
"""
# Insert after %changelog line
content = re.sub(
r'(%changelog\n)',
rf'\1{changelog_entry}',
content
)
# Write back
spec_file.write_text(content)
print(f"Updated {spec_path}:")
print(f" Version: {version}")
print(f" Release: 1%{{?dist}}")
print(f" JIRA: {jira_ticket}")
def main():
if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} <spec_file> <version> <jira_ticket> [author]", file=sys.stderr)
print(f"Example: {sys.argv[0]} bootc.spec 1.5.2 RHEL-104567", file=sys.stderr)
sys.exit(1)
spec_path = sys.argv[1]
version = sys.argv[2]
jira_ticket = sys.argv[3]
author = sys.argv[4] if len(sys.argv) > 4 else None
update_spec(spec_path, version, jira_ticket, author)
if __name__ == '__main__':
main()