Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 268 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
---
name: release
description: Guide through the Jumpstarter release process (branch, tag, operator bundle)
argument-hint: "Optional: version (e.g. '0.9.0-rc.1') or phase (e.g. 'operator-bundle')"
---

# Jumpstarter Release

You are guiding the user through the Jumpstarter project release process.

Before proceeding, read `.claude/rules/releasing-operator.md` for operator-specific details.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for the existence of both possible paths
fd -t f 'releasing-operator' .claude .cursor 2>/dev/null || find .claude .cursor -name '*releasing-operator*' -type f 2>/dev/null

Repository: jumpstarter-dev/jumpstarter

Length of output: 202


Correct the referenced file path.

The instruction in .claude/skills/release/SKILL.md refers to .claude/rules/releasing-operator.md, which does not exist. The actual rule file is located at .cursor/rules/releasing-operator.mdc. Update line 11 to reference the correct path:

Before proceeding, read `.cursor/rules/releasing-operator.mdc` for operator-specific details.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/release/SKILL.md at line 11, The referenced rule path in the
release skill instructions is incorrect, so update the guidance in SKILL.md to
point to the existing releasing-operator rule file. Use the existing instruction
text around the reference to `.claude/rules/releasing-operator.md` and change it
to the correct `.cursor/rules/releasing-operator.mdc` path so the release
workflow points to the right operator-specific documentation.


Release input: $ARGUMENTS

## Conventions

- **Git tags** use a `v` prefix: `v0.8.1`, `v0.9.0-rc.1`
- **Container image tags** do NOT use a `v` prefix: `:0.8.1`, `:0.9.0-rc.1`
- **RC tag format**: `vX.Y.Z-rc.N` (with dot before N). Reject old formats like `rc1` or `-rc1`.
- **RC-first rule**: When a release branch has no final release tags yet, the first tag MUST be an RC. Never tag a direct `vX.Y.0` final on a new branch without at least one RC first.
- Python packages are versioned automatically from git tags via `hatch-vcs` — no manual version files.
- The `bundle/` directory is NOT committed to the repo.
- `GITHUB_USER` env var controls the fork for community-operators (defaults to `mangelajo`).
- Do NOT modify `controller/deploy/operator/api/v1alpha1/jumpstarter_types.go` — the operator resolves `:latest` image defaults to its own version at runtime.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major

Contradiction: Cursor rule still instructs updating jumpstarter_types.go.

Your skill says "Do NOT modify jumpstarter_types.go", but .cursor/rules/releasing-operator.mdc still instructs users to update kubebuilder defaults in that file (lines 40–52). Since the PR summary explicitly mentions removing the jumpstarter_types.go update step as a v0.9.0-rc.1 improvement, the cursor rule is stale and will mislead users into making changes the skill forbids.

Update .cursor/rules/releasing-operator.mdc to remove the jumpstarter_types.go update instructions and CRD default verification grep that depends on it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/release/SKILL.md at line 24, The release guidance is
contradictory because the skill forbids changing jumpstarter_types.go while the
cursor rule still tells users to update kubebuilder defaults there. Update the
releasing-operator rule so it no longer instructs edits to
controller/deploy/operator/api/v1alpha1/jumpstarter_types.go, and remove the
related CRD default verification grep that depends on that file. Keep the rest
of the release workflow intact and ensure the instructions in
.cursor/rules/releasing-operator.mdc match the SKILL.md guidance.


## Ordering constraint

```
Makefile update → commit → tag push → [CI builds images] → GitHub Release → make bundle → make contribute
```

Images must exist before bundle generation. The skill enforces this by splitting the process into phases.

## Steps

### 1. Gather context and determine release type

Fetch the latest remote state first, then inspect:

```bash
git fetch origin --tags
git branch --show-current
git branch -r | grep 'origin/release-'
git tag --sort=-version:refname | head -20
gh release list --limit 5
grep -E '^(VERSION|REPLACES) ' controller/deploy/operator/Makefile
```

Using the git state and `$ARGUMENTS` (if provided), ask the user:

1. **What type of release is this?**
- **(A) Create a new release branch** — starting a new `X.Y` cycle from `main`
- **(B) Tag a release** — cut an RC or final from an existing release branch
- **(C) Operator bundle contribution** — generate and contribute the OLM bundle (after images are built)

2. **What version?** Suggest the next logical version based on existing tags. Examples:
- Latest tag is `v0.8.1` → suggest `v0.9.0-rc.1` for a new branch, or `v0.8.2-rc.1` for a patch
- Latest RC is `v0.9.0-rc.2` → suggest `v0.9.0-rc.3` or `v0.9.0` (final)

3. **Validate the version:**
- Format must be `vX.Y.Z` or `vX.Y.Z-rc.N`
- If this is a final release (`vX.Y.Z` without `-rc`), verify that at least one `vX.Y.Z-rc.*` tag exists. If not, warn the user and suggest creating an RC first.
- If creating a new release branch, the first tag must be an RC (e.g., `vX.Y.0-rc.1`)

### 2A. Create a new release branch

Only if the user selected type (A).

**Important:** The `release-*` branch pattern may be protected by repository rulesets. If a direct push is rejected, try creating the branch via the GitHub API:

```bash
# Try direct push first
git fetch origin
git checkout -b release-X.Y origin/main
git push origin release-X.Y

# If rejected by branch protection, use the API:
SHA=$(git rev-parse origin/main)
gh api repos/{owner}/{repo}/git/refs -f ref=refs/heads/release-X.Y -f sha="$SHA"
```
Comment on lines +78 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Replace template placeholder with actual repository or dynamic lookup.

repos/{owner}/{repo} appears in an executable code block but uses placeholder syntax that will 404 if copied literally. Use the actual repository path or derive it dynamically:

- gh api repos/{owner}/{repo}/git/refs -f ref=refs/heads/release-X.Y -f sha="$SHA"
+ gh api repos/jumpstarter-dev/jumpstarter/git/refs -f ref=refs/heads/release-X.Y -f sha="$SHA"

Or derive from the local remote:

REPO=$(gh repo view --json owner,name -q '.owner.login + "/" + .name')
gh api "repos/${REPO}/git/refs" -f ref=refs/heads/release-X.Y -f sha="$SHA"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/release/SKILL.md around lines 78 - 80, The release SKILL
example still uses the literal `repos/{owner}/{repo}` placeholder in the `gh
api` command, which will fail if copied as-is. Update the executable snippet in
the release workflow guidance to use a real repository path or a dynamic lookup
via `gh repo view`, and make sure the `gh api .../git/refs` example references
the resolved repository variable rather than placeholder syntax.


If both fail, the user needs to temporarily remove `release-*` from the branch protection ruleset, push the branch, then re-add it.

After pushing, inform the user:
- The branch push triggers CI to build images tagged with the branch name (e.g., `:release-0.9`)
- The next step is to tag the first RC from this branch
- Offer to continue immediately with step 2B to tag `vX.Y.0-rc.1`

### 2B. Tag a release

Only if the user selected type (B), or continuing from step 2A.

#### Phase 1: Pre-tag code changes

Ensure you are on the correct `release-X.Y` branch:

```bash
git fetch origin
git checkout release-X.Y
git pull origin release-X.Y
```

**Update version references:**

1. **`controller/deploy/operator/Makefile`** — update:
- `VERSION ?= X.Y.Z` (or `X.Y.Z-rc.N` for RCs, no `v` prefix)
- `REPLACES ?= jumpstarter-operator.vPREVIOUS` — must point to the most recently published version in the OLM channel (including RCs). Check existing tags to determine the correct value. For the first release on a new branch, check what the last published version was across all branches.

2. **Regenerate manifests:**
```bash
cd controller/deploy/operator
make manifests generate
```

3. **Commit and push** the changes to the release branch. Files to include:
- `controller/deploy/operator/Makefile`
- Any files changed by `make manifests generate`

#### Phase 2: Tag and GitHub Release

4. **Create and push the git tag:**
```bash
git tag vX.Y.Z # or vX.Y.Z-rc.N
git push origin vX.Y.Z
```

5. **Create the GitHub Release:**
```bash
# For a release candidate:
gh release create vX.Y.Z-rc.N \
--title "vX.Y.Z-rc.N" \
--prerelease \
--generate-notes \
--notes-start-tag vPREVIOUS_TAG

# For a final release:
gh release create vX.Y.Z \
--title "vX.Y.Z" \
--generate-notes \
--notes-start-tag vPREVIOUS_TAG
```
Use the previous tag as `--notes-start-tag` to scope the generated release notes.

#### Phase 3: Wait for CI

The tag push triggers:
- `build-images.yaml` — builds and pushes all container images
- `trigger-packages.yaml` — regenerates the Python package index

The GitHub Release triggers:
- `release-operator-installer.yaml` — uploads `operator-installer.yaml` to the release

Tell the user that CI is now building the container images and you will monitor progress.

Find the CI run triggered by the tag and monitor it:
```bash
# Find the run triggered by the tag push
gh run list --workflow=build-images.yaml --limit 3 --json databaseId,status,conclusion,headBranch,event,createdAt

# Watch the run until it completes (use the databaseId from above)
gh run watch <RUN_ID>
```

Monitor the run periodically using `gh run view <RUN_ID> --json status,conclusion` until it completes. If it fails, offer to re-trigger with `gh run rerun <RUN_ID>`. If it fails again, show the user the failure details with `gh run view <RUN_ID> --log-failed` and stop.

When the build-images workflow succeeds, inform the user and proceed automatically to step 2C.

### 2C. Operator bundle contribution

Only if the user selected type (C), or continuing after step 2B.

#### Verify images exist

Before generating the bundle, confirm the container images for this version are available:

```bash
gh run list --workflow=build-images.yaml --limit 5
```

The user can also check `quay.io/jumpstarter-dev/jumpstarter-controller:X.Y.Z` directly.

#### Generate the OLM bundle

```bash
cd controller/deploy/operator
make bundle
```

#### Verify the bundle output

```bash
# Image references should show :X.Y.Z (no :latest, no :vX.Y.Z)
grep -E "containerImage|image: quay" controller/deploy/operator/bundle/manifests/jumpstarter-operator.clusterserviceversion.yaml

# Release config
cat controller/deploy/operator/bundle/release-config.yaml
```

Show the output to the user and ask them to confirm it looks correct before continuing.

#### Contribute to community-operators

```bash
cd controller/deploy/operator

# Set GITHUB_USER if different from default (mangelajo):
# export GITHUB_USER=yourusername

# AUTO_CONFIRM=1 skips the interactive y/N prompt
AUTO_CONFIRM=1 make contribute
```

After the script completes, push to the fork and create PRs using `gh`:

```bash
BRANCH="jumpstarter-operator-release-X.Y.Z"

cd controller/deploy/operator/contribute/community-operators
git push -f user "$BRANCH"

cd ../community-operators-prod
git push -f user "$BRANCH"
```

Then create PRs on both repos:

```bash
# PR for community-operators
cd controller/deploy/operator/contribute/community-operators
gh pr create --repo k8s-operatorhub/community-operators \
--title "operator jumpstarter-operator (X.Y.Z)" \
--body "Release X.Y.Z of the jumpstarter-operator for the alpha channel." \
--head GITHUB_USER:$BRANCH --base main

# PR for community-operators-prod
cd ../community-operators-prod
gh pr create --repo redhat-openshift-ecosystem/community-operators-prod \
--title "operator jumpstarter-operator (X.Y.Z)" \
--body "Release X.Y.Z of the jumpstarter-operator for the alpha channel." \
--head GITHUB_USER:$BRANCH --base main
```

Replace `GITHUB_USER` with the actual GitHub username (default: `mangelajo`).
Comment on lines +230 to +243

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix GITHUB_USER environment variable expansion in PR commands.

The --head flag uses the literal string GITHUB_USER rather than the environment variable. Bash won't expand it unless prefixed with $:

- --head GITHUB_USER:$BRANCH --base main
+ --head ${GITHUB_USER}:$BRANCH --base main

And add the export reminder before both commands:

export GITHUB_USER=${GITHUB_USER:-mangelajo}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/release/SKILL.md around lines 230 - 243, The PR creation
commands in the release guide are using the literal GITHUB_USER token in the gh
pr create --head argument instead of expanding the environment variable. Update
the command examples so they reference the shell variable form consistently in
both community-operators and community-operators-prod flows, and add the export
reminder before these commands in the release workflow section using the same
GITHUB_USER symbol so readers can copy/paste it correctly.


### 3. Post-release steps

#### Add new release branch to protection ruleset

If a new `release-X.Y` branch was created, remind the user to add it to the repository's branch protection ruleset so it requires merge queue for future changes. This is done in GitHub Settings → Rules → Rulesets → select the main/release ruleset → add `release-X.Y` to the branch targeting pattern.

#### Cherry-pick infrastructure fixes

If the release included infrastructure-only changes to the contribute script or Makefile (not version-specific), cherry-pick them to `main` in a separate PR.

#### Checklist

Present a checklist of what was done (mark completed items) and what remains:

- [ ] Release branch created (if new `X.Y` cycle)
- [ ] Release branch added to protection ruleset (if new branch)
- [ ] Operator Makefile VERSION and REPLACES updated
- [ ] Git tag created and pushed
- [ ] GitHub Release created (with `--prerelease` for RCs)
- [ ] CI image build completed
- [ ] `operator-installer.yaml` asset uploaded (automated by CI)
- [ ] OLM bundle generated and verified (`make bundle`)
- [ ] Community-operators PRs created (`make contribute` + `gh pr create`)
- [ ] Infrastructure fixes cherry-picked to `main` (if applicable)
10 changes: 2 additions & 8 deletions .cursor/rules/releasing-operator.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ Image string `json:"image,omitempty"`
```
These defaults flow into the CRD YAML via `make manifests` and into the OLM bundle via `make bundle`.

### 3. `controller/deploy/operator/test/e2e/e2e_test.go`
Update the default controller image constant:
```go
const defaultControllerImage = "quay.io/jumpstarter-dev/jumpstarter-controller:X.Y.Z"
```

### 4. `controller/deploy/operator/config/manager/kustomization.yaml`
### 3. `controller/deploy/operator/config/manager/kustomization.yaml`
The `newTag` field is updated automatically by `make bundle` (via `kustomize edit set image`), but verify it reflects the correct tag after running the bundle target.

## Release Steps
Expand Down Expand Up @@ -91,7 +85,7 @@ cat bundle/release-config.yaml
```

### Step 4: Commit
Create a commit with the Makefile, types, CRD, kustomization, and e2e changes. The `bundle/` directory is NOT committed to the repo (it was removed in commit `12b680e3`).
Create a commit with the Makefile, types, CRD, and kustomization changes. The `bundle/` directory is NOT committed to the repo (it was removed in commit `12b680e3`).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove "types" from commit description.

The commit description still lists "types" (referring to jumpstarter_types.go) among the files to commit. Since that file should no longer be modified, remove it from the description:

- Create a commit with the Makefile, types, CRD, and kustomization changes.
+ Create a commit with the Makefile, CRD, and kustomization changes.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Create a commit with the Makefile, types, CRD, and kustomization changes. The `bundle/` directory is NOT committed to the repo (it was removed in commit `12b680e3`).
Create a commit with the Makefile, CRD, and kustomization changes. The `bundle/` directory is NOT committed to the repo (it was removed in commit `12b680e3`).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/rules/releasing-operator.mdc at line 88, The commit description
still mentions “types” as part of the files to commit, but that item should be
removed from the release instructions. Update the commit description in
releasing-operator.mdc so it only references the intended Makefile, CRD, and
kustomization changes, and keep the guidance consistent with the fact that
bundle/ is not committed.


### Step 5: Contribute to community-operators (when ready)
```bash
Expand Down
Loading