diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..1ddb728 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,85 @@ +name: Release + +on: + workflow_dispatch: + inputs: + tag: + description: "New version to release, e.g. 0.4.0 (no 'v' prefix)" + required: true + type: string + version-replace: + description: "Previously shipped version for the OLM replaces chain, e.g. 0.3.2" + required: true + type: string + +permissions: + contents: read + +jobs: + prepare-release: + name: Prepare release PR + runs-on: ubuntu-latest + concurrency: + group: release-preparation + cancel-in-progress: true + steps: + - name: Validate inputs + env: + TAG: ${{ inputs.tag }} + VERSION_REPLACE: ${{ inputs.version-replace }} + run: | + SEMVER_REGEX='^[0-9]+\.[0-9]+\.[0-9]+$' + [[ "$TAG" =~ $SEMVER_REGEX ]] || { + echo "::error title=Invalid input::tag='$TAG' must be MAJOR.MINOR.PATCH (e.g. 0.4.0, no 'v' prefix)" + exit 1 + } + [[ "$VERSION_REPLACE" =~ $SEMVER_REGEX ]] || { + echo "::error title=Invalid input::version-replace='$VERSION_REPLACE' must be MAJOR.MINOR.PATCH (e.g. 0.3.2)" + exit 1 + } + + - name: Checkout + uses: actions/checkout@v6 + with: + # NOTE: we need to fetch the whole history to be able to generate the changelog + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install tools + uses: ./.github/tools-cache + + - name: Update VERSION + run: echo "${{ inputs.tag }}" > VERSION + + - name: Generate changelog + run: make generate-changelog + + - name: Generate bundle + env: + VERSION_REPLACED: ${{ inputs.version-replace }} + run: make bundle + + - name: Generate installer manifest + run: make build-installer + + - name: Create pull request + # NOTE: BOT_TOKEN (PAT for persesbot) is required: a PR opened with the default + # GITHUB_TOKEN would not trigger downstream pull_request workflows. + uses: peter-evans/create-pull-request@v8 + with: + token: ${{ secrets.BOT_TOKEN }} + branch: chore-version-${{ inputs.tag }} + delete-branch: true + signoff: true + commit-message: "chore: update VERSION to ${{ inputs.tag }}" + title: "chore: update VERSION to ${{ inputs.tag }}" + body: | + Automated release PR for `v${{ inputs.tag }}` + - `VERSION` bumped to `${{ inputs.tag }}` + - Changelog generated (`CHANGELOG.md`) and ready for `UNKNOWN` cleanup in PR review + - Bundle generated with `VERSION_REPLACED=${{ inputs.version-replace }}` (OLM `replaces: perses-operator.v${{ inputs.version-replace }}`) + - Installer manifest `bundle.yaml` generated diff --git a/RELEASE.md b/RELEASE.md index c1d1e66..700d50a 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,83 +1,127 @@ # Perses Operator Release -## 1. Prepare your release - -- fetch the latest changes from `main` - -- Create a release branch named `release/v.` from the `main` branch. - -> ⚠️ Release candidates and patch releases for any given major or minor release happen in the same `release/v.` branch. Do not create `release/` for patch or release candidate releases. - -- Create a branch based on the release branch you just created in the step above in your fork. The branch should use the naming pattern `/release-v..`. -- Update the file `VERSION` with the new version to be created. -- Generate `CHANGELOG.md` updates based on git history: - - ```bash - make generate-changelog - ``` - -- Regenerate bundle, jsonnet, and installer files, and verify they are up to date: - - ```bash - make bundle-check - make installer-check - ``` - -- Review the generated `CHANGELOG.md` for valid output. Things to check include: - - Entries in the `CHANGELOG.md` are meant to be in this order: - * `[FEATURE]` - * `[ENHANCEMENT]` - * `[BUGFIX]` - * `[BREAKINGCHANGE]` - * `[DOC]` - - Entries that map to a pull request should include a pull request number. - - As we have many libraries we publish, it's better if you also put a clear indication about what library is affected by - these changes. - - Consumers understand how to handle breaking changes either through the messaging in the changelog or through the linked pull requests. -- Push the branch to Github and create a pull request with the release branch as the base. This gives others the opportunity to chime in on the release, - in general, and on the addition to the changelog, in particular. - - It's also helpful to drop a link to the release PR in #perses-dev on the CNCF Slack to get extra visibility. -- Address any necessary feedback. -- Once the pull request is approved, merge it into the release branch. - -## 2. Create release tag and validate release - -- Pull down the latest updates to the release branch on your local machine to ensure you have the updates from the previous step. -- Tag the new release via the following commands: - - ```bash - git checkout release/v. - export GIT_REMOTE_UPSTREAM=origin # change if your upstream remote differs - make tag - git push $GIT_REMOTE_UPSTREAM v.. - ``` - -Once a tag is created, an automated release process for this tag is triggered via Github Actions. This automated process includes: - -- Building new go binaries and docker images. -- Publishing the docker images to Docker Hub. -- Creating a new Github release that uses the changelog as the release notes and provides tarballs with the latest go binaries. - -## 3. Merge the release into `main` - -It can be helpful to leave the release branch up for a little while in case we need to create a patch release to address bugs or minor issues with the release you just made. - -Once the release branch is no longer needed, you should open a new PR based on `main` to merge those changes. When this PR is approved, merge it into `main` :warning: **using the "merge pull request" option, not "squash and merge"** (the latter would delete the commit needed for the release tag, which can lead to problems). +The release is driven by the **Release** GitHub Actions workflow +(`.github/workflows/release.yaml`). It opens a PR against `main` that bumps +`VERSION`, regenerates `CHANGELOG.md`, regenerates the OLM bundle with the +correct `replaces` chain, and regenerates the installer manifest. Publishing +runs after the PR is merged and the new `v*` tag is pushed. + +## Flow overview + +``` +1. User: Actions ▸ Release ▸ Run workflow (tag, version-replace) + │ + ▼ +2. release.yaml → opens PR: chore: update VERSION to + │ (VERSION + CHANGELOG.md + bundle/ + bundle.yaml) + ▼ +3. User: review & merge PR into main + │ + ▼ +4. User: push v tag on main + │ + ▼ +5. ci.yaml (on v* tag) → images (operator/bundle/catalog) + GitHub release + │ + ▼ +6. publish-operator-hub.yaml (on release: published) + → PR against k8s-operatorhub/community-operators +``` + +## 1. Trigger the release workflow + +In GitHub: **Actions** → **Release** → **Run workflow**. + +Inputs: + +| Input | Example | Meaning | +|-------------------|---------|--------------------------------------------------------------------| +| `tag` | `0.4.0` | New version to release (no `v` prefix). | +| `version-replace` | `0.3.2` | Previously shipped version. Drives OLM `spec.replaces` in the CSV. | + +The workflow: + +- Writes `` to `VERSION`. +- Runs `make generate-changelog` — regenerates `CHANGELOG.md` for the new + release version. +- Runs `make bundle VERSION_REPLACED=` — regenerates `bundle/manifests/*` + (CSV `spec.version`, `spec.replaces: perses-operator.v`) and the + jsonnet files under `jsonnet/generated` and `jsonnet/examples`. +- Runs `make build-installer` — regenerates the root `bundle.yaml` installer. +- Opens a PR titled `chore: update VERSION to ` against `main` on branch + `chore-version-` + +Re-running the workflow with the same `tag` updates the existing PR branch. + +> ⚠️ `version-replace` must equal the version currently shipped on OperatorHub + +## 2. Review and merge the PR + +- Review the diff — specifically: + - `VERSION` + - `CHANGELOG.md` (clean up/categorize any `UNKNOWN` entries) + - `bundle/manifests/perses-operator.clusterserviceversion.yaml` + (`spec.version`, `spec.replaces`) + - CRDs and jsonnet generated files (if CRDs changed) + - `bundle.yaml` (root installer manifest) +- Approve and merge the PR into `main`. + +> Note: the PR is created using the `BOT_TOKEN` PAT (the same token used by +> `publish-operator-hub.yaml`). This is required so downstream `pull_request` +> workflows (`go.yaml`, `ci.yaml`) fire on the auto-created PR. `BOT_TOKEN` +> is a configuration prerequisite (see +> [One-time setup prerequisites](#one-time-setup-prerequisites)). + +## 3. Tag the release + +Pull the merged `main` and push the tag: + +```bash +git fetch origin +git checkout main +git pull +git tag -a v -m "v" +git push origin v +``` + +> ⚠️ Do **not** use GitHub's "Create release" UI. `ci.yaml` drives `goreleaser` +> which publishes the GitHub release itself — creating one manually would race. + +Pushing the `v*` tag triggers `ci.yaml`, which: + +- Builds and pushes the operator container image to Docker Hub and Quay. +- Builds and pushes the bundle and catalog images (`v` tag on both registries). +- Runs `goreleaser` to publish the GitHub release with the Go binaries. ## 4. Publish to OperatorHub (automated) -When the GitHub release is published, a workflow automatically creates a pull request to submit the new operator version to [k8s-operatorhub/community-operators](https://github.com/k8s-operatorhub/community-operators/pulls) (OperatorHub.io). +When the GitHub release is published, `publish-operator-hub.yaml` fires and +calls the reusable `operator-hub-release.yaml` workflow. It: + +- Checks out the `persesbot` fork of `k8s-operatorhub/community-operators` (synced with upstream). +- Checks out `perses-operator` at the released tag and runs `make bundle`. +- Copies `bundle/{manifests,metadata,tests}` into `operators/perses-operator//`. +- Pushes the branch to the `persesbot` fork and opens a PR against + [`k8s-operatorhub/community-operators`](https://github.com/k8s-operatorhub/community-operators/pulls). -A maintainer should monitor the PR and address any CI feedback from the community-operators repository. +Monitor that PR and address any feedback from the community-operators CI. ### One-time setup prerequisites -Before the automation can run, the following must be configured once: +Before the OperatorHub automation can run, the following must be configured once: -1. **Bot account**: The `persesbot` GitHub account must have a fork of [k8s-operatorhub/community-operators](https://github.com/k8s-operatorhub/community-operators). -2. **GitHub secret**: A Personal Access Token with `repo` scope for the bot account must be added as `PERSESBOT_GITHUB_TOKEN` in the repository settings. -3. **CLA/DCO**: The bot account should sign the CNCF CLA or Linux Foundation DCO if required. +1. **Bot account**: the `persesbot` GitHub account must have a fork of + [`k8s-operatorhub/community-operators`](https://github.com/k8s-operatorhub/community-operators). +2. **GitHub secret**: a Personal Access Token (`repo` scope) for the bot account + must be added as `BOT_TOKEN` in the repository settings. +3. **CLA/DCO**: the bot account should sign the CNCF CLA or Linux Foundation DCO + if required. ## 5. Update the Helm chart -After the release is published, update the [Perses Operator Helm chart](https://github.com/perses/helm-charts/tree/main/charts/perses-operator) in the [perses/helm-charts](https://github.com/perses/helm-charts) repository. Follow the [Bumping perses-operator Version](https://github.com/perses/helm-charts/blob/main/DEVELOPER_GUIDE.md#bumping-perses-operator-version) guide. +After the release is published, update the +[Perses Operator Helm chart](https://github.com/perses/helm-charts/tree/main/charts/perses-operator) +in the [`perses/helm-charts`](https://github.com/perses/helm-charts) repository. +Follow the +[Bumping perses-operator Version](https://github.com/perses/helm-charts/blob/main/DEVELOPER_GUIDE.md#bumping-perses-operator-version) +guide.