From f8bd4576cfefd105a998249bfdb719e391a92dd2 Mon Sep 17 00:00:00 2001 From: gursewak1997 Date: Mon, 6 Jul 2026 16:30:22 -0700 Subject: [PATCH] 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 --- .gitlab-ci.yml | 53 +++++++ AUTOMATION-SETUP.md | 216 ++++++++++++++++++++++++++ scripts/README.md | 242 ++++++++++++++++++++++++++++++ scripts/check-upstream-release.sh | 65 ++++++++ scripts/create-jira.sh | 102 +++++++++++++ scripts/create-release-mr.sh | 234 +++++++++++++++++++++++++++++ scripts/test-workflow.sh | 142 ++++++++++++++++++ scripts/update-spec.py | 86 +++++++++++ 8 files changed, 1140 insertions(+) create mode 100644 .gitlab-ci.yml create mode 100644 AUTOMATION-SETUP.md create mode 100644 scripts/README.md create mode 100755 scripts/check-upstream-release.sh create mode 100755 scripts/create-jira.sh create mode 100755 scripts/create-release-mr.sh create mode 100755 scripts/test-workflow.sh create mode 100755 scripts/update-spec.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..33d89af --- /dev/null +++ b/.gitlab-ci.yml @@ -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 diff --git a/AUTOMATION-SETUP.md b/AUTOMATION-SETUP.md new file mode 100644 index 0000000..a8caa30 --- /dev/null +++ b/AUTOMATION-SETUP.md @@ -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 + 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. diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..cc96d63 --- /dev/null +++ b/scripts/README.md @@ -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 [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 [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 " +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 diff --git a/scripts/check-upstream-release.sh b/scripts/check-upstream-release.sh new file mode 100755 index 0000000..687437c --- /dev/null +++ b/scripts/check-upstream-release.sh @@ -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 < release.env < [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 <&2 + exit 1 + fi +else + log_error "API request failed with HTTP $HTTP_CODE" + echo "$BODY" >&2 + exit 1 +fi diff --git a/scripts/create-release-mr.sh b/scripts/create-release-mr.sh new file mode 100755 index 0000000..d91925d --- /dev/null +++ b/scripts/create-release-mr.sh @@ -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 <&2 + exit 1 +fi + +# Cleanup +rm -rf /tmp/bootc-release + +log_info "Release automation complete!" diff --git a/scripts/test-workflow.sh b/scripts/test-workflow.sh new file mode 100755 index 0000000..f9c4527 --- /dev/null +++ b/scripts/test-workflow.sh @@ -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 "; 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 diff --git a/scripts/update-spec.py b/scripts/update-spec.py new file mode 100755 index 0000000..2ce0df6 --- /dev/null +++ b/scripts/update-spec.py @@ -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 " + + 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]} [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()