From e64f3d998bedeb201ae21e571304cbb97e737f50 Mon Sep 17 00:00:00 2001 From: Lokananda Prabhu Date: Tue, 29 Sep 2026 10:23:05 +0530 Subject: [PATCH] chore: sync Version Packages workflow for release-x.y branches (#4173) Co-authored-by: Cursor --- .../workflows/release_workspace_version.yml | 347 +++++++++++++++--- CONTRIBUTING.md | 83 +++-- 2 files changed, 347 insertions(+), 83 deletions(-) diff --git a/.github/workflows/release_workspace_version.yml b/.github/workflows/release_workspace_version.yml index 0cc02d70823..0e1f039cfcf 100644 --- a/.github/workflows/release_workspace_version.yml +++ b/.github/workflows/release_workspace_version.yml @@ -1,79 +1,134 @@ name: Prior Version Release Workspace on: - # ADDED: Only trigger on PR closed event on workspace/** branch. - # The assumption is that branch protection rules and CODEOWNERS are configured. - pull_request: types: - closed branches: - - 'workspace/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + - 'release-1.*/*' + - 'release-*.*' jobs: - # ADDED: Checks if PR is a Version Packages PR on workspace/** branch - # and validates PR title, author, branch and merged status. check-merged-pr: - name: Check if PR is a Version Packages PR on workspace/** branch + name: Classify prior-version release PR runs-on: ubuntu-latest outputs: - is_version_pr: ${{ steps.check_pr.outputs.is_version_pr }} - workspace_name: ${{ steps.extract_workspace.outputs.workspace_name }} + is_version_pr: ${{ steps.check_pr.outputs.is_version_pr }} + is_merged: ${{ steps.check_pr.outputs.is_merged }} + is_repository_release: ${{ steps.classify_branch.outputs.is_repository_release }} + is_supported_branch: ${{ steps.classify_branch.outputs.is_supported_branch }} + workspace_name: ${{ steps.extract_workspace.outputs.workspace_name }} + version_branch_id: ${{ steps.extract_workspace.outputs.version_branch_id }} steps: - - name: Check PR title, author, branch and merged status - id: check_pr - env: - PR_TITLE: ${{ github.event.pull_request.title }} - HEAD_REF: ${{ github.event.pull_request.head.ref }} - USER_LOGIN: ${{ github.event.pull_request.user.login }} - PR_MERGED: ${{ github.event.pull_request.merged }} - run: | - if [[ "$PR_TITLE" == Version*Packages* \ - && "$USER_LOGIN" == "rhdh-bot" ]] \ - && [[ "$HEAD_REF" == maintenance-changesets-release/* ]] \ - && [[ "$PR_MERGED" == "true" ]]; then - echo "is_version_pr=true" >> $GITHUB_OUTPUT - else - echo "is_version_pr=false" >> $GITHUB_OUTPUT + - name: Check PR title, author, branch and merged status + id: check_pr + env: + PR_TITLE: ${{ github.event.pull_request.title }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + USER_LOGIN: ${{ github.event.pull_request.user.login }} + PR_MERGED: ${{ github.event.pull_request.merged }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + run: | + if [[ "$PR_MERGED" == "true" ]]; then + echo "is_merged=true" >> "$GITHUB_OUTPUT" + else + echo "is_merged=false" >> "$GITHUB_OUTPUT" + fi + + is_version_pr=false + if [[ "$PR_TITLE" == "Version Packages"* \ + && "$USER_LOGIN" == "rhdh-bot" \ + && "$PR_MERGED" == "true" ]]; then + if [[ "$BASE_REF" =~ ^release-[0-9]+\.[0-9]+$ ]]; then + if [[ "$HEAD_REF" =~ ^maintenance-changesets-release/${BASE_REF}/[^/]+$ ]]; then + is_version_pr=true + fi + elif [[ "$BASE_REF" =~ ^release-1\.[0-9]+/[^/]+$ ]]; then + if [[ "$HEAD_REF" == "maintenance-changesets-release/$BASE_REF" ]]; then + is_version_pr=true fi + fi + fi + echo "is_version_pr=$is_version_pr" >> "$GITHUB_OUTPUT" + + - name: Classify target branch + id: classify_branch + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} + run: | + if [[ "$BASE_REF" =~ ^release-([0-9]+)\.[0-9]+$ ]]; then + RELEASE_MAJOR="${BASH_REMATCH[1]}" + if (( RELEASE_MAJOR >= 2 )); then + echo "is_repository_release=true" >> "$GITHUB_OUTPUT" + echo "is_supported_branch=true" >> "$GITHUB_OUTPUT" + echo "::notice title=Release workflow classification::$BASE_REF uses the repository-wide release path." + else + echo "is_repository_release=false" >> "$GITHUB_OUTPUT" + echo "is_supported_branch=false" >> "$GITHUB_OUTPUT" + echo "::warning title=Release workflow skipped::$BASE_REF is not a supported 1.x per-workspace branch or 2.x+ repository-wide branch. Use release-1.x/ for legacy releases." + fi + elif [[ "$BASE_REF" =~ ^release-1\.[0-9]+/[^/]+$ ]]; then + echo "is_repository_release=false" >> "$GITHUB_OUTPUT" + echo "is_supported_branch=true" >> "$GITHUB_OUTPUT" + echo "::notice title=Release workflow classification::$BASE_REF uses the legacy per-workspace release path." + else + echo "is_repository_release=false" >> "$GITHUB_OUTPUT" + echo "is_supported_branch=false" >> "$GITHUB_OUTPUT" + echo "::warning title=Release workflow skipped::$BASE_REF does not match release-1.x/ or release-2.x+." + fi - # ADDED: Extracts workspace name from branch, ensuring it is a workspace/** branch - - name: Extract Workspace name from branch - id: extract_workspace - env: - BASE_REF: ${{ github.event.pull_request.base.ref }} - run: | - WORKSPACE_NAME=$(echo "$BASE_REF" | cut -d'/' -f2) - echo "workspace_name=$WORKSPACE_NAME" >> $GITHUB_OUTPUT + - name: Extract workspace name from branch + id: extract_workspace + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + run: | + if [[ "$BASE_REF" =~ ^release-1\.[0-9]+/[^/]+$ ]]; then + WORKSPACE_NAME=$(echo "$BASE_REF" | cut -d'/' -f2) + VERSION_BRANCH_ID="$BASE_REF" + elif [[ "$BASE_REF" =~ ^release-[0-9]+\.[0-9]+$ && "$HEAD_REF" =~ ^maintenance-changesets-release/${BASE_REF}/[^/]+$ ]]; then + WORKSPACE_NAME=$(echo "$HEAD_REF" | cut -d'/' -f3) + VERSION_BRANCH_ID="$BASE_REF/$WORKSPACE_NAME" + else + WORKSPACE_NAME="" + VERSION_BRANCH_ID="" + fi + echo "workspace_name=$WORKSPACE_NAME" >> "$GITHUB_OUTPUT" + echo "version_branch_id=$VERSION_BRANCH_ID" >> "$GITHUB_OUTPUT" + if [[ -n "$WORKSPACE_NAME" ]]; then + echo "::notice title=Release workspace::$WORKSPACE_NAME (version branch ID: $VERSION_BRANCH_ID)" + else + echo "::warning title=Release workspace unavailable::Could not derive a workspace from base '$BASE_REF' and head '$HEAD_REF'. Release jobs will be skipped." + fi changesets-pr: - name: Update Version Packages PR for ${{ needs.check-merged-pr.outputs.workspace_name }} on branch ${{ github.ref }} + name: LEGACY - Update Version Packages PR runs-on: ubuntu-latest needs: check-merged-pr - if: needs.check-merged-pr.outputs.is_version_pr == 'false' + if: needs.check-merged-pr.outputs.is_supported_branch == 'true' && needs.check-merged-pr.outputs.is_version_pr == 'false' && needs.check-merged-pr.outputs.is_repository_release == 'false' && needs.check-merged-pr.outputs.is_merged == 'true' + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} defaults: run: working-directory: ./workspaces/${{ needs.check-merged-pr.outputs.workspace_name }} env: CI: true NODE_OPTIONS: --max-old-space-size=4096 + YARN_ENABLE_SCRIPTS: false outputs: needs_release: ${{ steps.release_check.outputs.needs_release }} steps: - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: - ref: ${{ github.ref }} + ref: ${{ github.ref }} - name: Verify maintenance-changesets-release branch does not exist env: - WORKSPACE_NAME: ${{ needs.check-merged-pr.outputs.workspace_name }} + VERSION_BRANCH_ID: ${{ needs.check-merged-pr.outputs.version_branch_id }} run: | - if git ls-remote --exit-code origin "refs/heads/maintenance-changesets-release/$WORKSPACE_NAME"; then - echo "Error: maintenance-changesets-release/$WORKSPACE_NAME branch already exists. Please clean up the branch before proceeding." + if git ls-remote --exit-code origin "refs/heads/maintenance-changesets-release/$VERSION_BRANCH_ID"; then + echo "Error: maintenance-changesets-release/$VERSION_BRANCH_ID branch already exists. Please clean up the branch before proceeding." exit 1 fi @@ -86,9 +141,9 @@ jobs: - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT - + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles(format('workspaces/${0}/**/yarn.lock', needs.check-merged-pr.outputs.workspace_name)) }} @@ -126,16 +181,126 @@ jobs: title: Version Packages (${{ needs.check-merged-pr.outputs.workspace_name }}) cwd: workspaces/${{ needs.check-merged-pr.outputs.workspace_name }} version: yarn changeset version - versionBranch: maintenance-changesets-release/${{ needs.check-merged-pr.outputs.workspace_name }} + versionBranch: maintenance-changesets-release/${{ needs.check-merged-pr.outputs.version_branch_id }} + skipRootChangelogUpdate: true + env: + GITHUB_TOKEN: ${{ secrets.RHDH_BOT_TOKEN }} + + repository-release-workspaces: + name: Find changed workspaces + runs-on: ubuntu-latest + needs: check-merged-pr + if: needs.check-merged-pr.outputs.is_supported_branch == 'true' && needs.check-merged-pr.outputs.is_repository_release == 'true' && needs.check-merged-pr.outputs.is_version_pr == 'false' && needs.check-merged-pr.outputs.is_merged == 'true' + outputs: + workspaces: ${{ steps.find-workspaces.outputs.workspaces }} + steps: + - name: Checkout merged PR + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + ref: ${{ github.event.pull_request.merge_commit_sha }} + fetch-depth: 0 + + - name: Find changed workspaces + id: find-workspaces + run: node scripts/ci/list-workspaces-with-changes.js + env: + COMMIT_SHA_BEFORE: ${{ github.event.pull_request.base.sha }} + INCLUDE_NOOP: 'false' + + repository-changesets-pr: + name: Update Version Packages PR + runs-on: ubuntu-latest + needs: + - check-merged-pr + - repository-release-workspaces + if: needs.repository-release-workspaces.outputs.workspaces != '[]' + concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.base.ref }}-${{ matrix.workspace }} + strategy: + fail-fast: false + matrix: + workspace: ${{ fromJSON(needs.repository-release-workspaces.outputs.workspaces) }} + defaults: + run: + working-directory: ./workspaces/${{ matrix.workspace }} + env: + CI: true + NODE_OPTIONS: --max-old-space-size=4096 + YARN_ENABLE_SCRIPTS: false + steps: + - name: Checkout merged PR + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + ref: ${{ github.event.pull_request.merge_commit_sha }} + fetch-depth: 0 + + - name: Verify maintenance-changesets-release branch does not exist + env: + WORKSPACE: ${{ matrix.workspace }} + RELEASE_BRANCH: ${{ github.event.pull_request.base.ref }} + run: | + VERSION_BRANCH_ID="$RELEASE_BRANCH/$WORKSPACE" + if git ls-remote --exit-code origin "refs/heads/maintenance-changesets-release/$VERSION_BRANCH_ID"; then + echo "Error: maintenance-changesets-release/$VERSION_BRANCH_ID branch already exists. Please clean up the branch before proceeding." + exit 1 + fi + + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 24 + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT + + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles(format('workspaces/${0}/**/yarn.lock', matrix.workspace)) }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: yarn install + run: yarn install --immutable + + - name: Fetch previous commit for release check + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: git fetch origin "$BASE_SHA" + + - name: Check if release + id: release_check + run: | + yarn install + node scripts/ci/check-if-release.js + working-directory: ./ + env: + WORKSPACE_NAME: ${{ matrix.workspace }} + COMMIT_SHA_BEFORE: '${{ github.event.pull_request.base.sha }}' + TARGET_BRANCH: ${{ github.event.pull_request.merge_commit_sha }} + + - name: Update Version Packages (${{ matrix.workspace }}) PR + if: steps.release_check.outputs.needs_release != 'true' + uses: backstage/changesets-action@a39baf18913e669734ffb00c2fd9900472cfa240 # v2.3.2 + with: + title: Version Packages (${{ matrix.workspace }}) + cwd: workspaces/${{ matrix.workspace }} + version: yarn changeset version + versionBranch: maintenance-changesets-release/${{ github.event.pull_request.base.ref }}/${{ matrix.workspace }} skipRootChangelogUpdate: true env: GITHUB_TOKEN: ${{ secrets.RHDH_BOT_TOKEN }} release: - name: Prior Version Release workspace ${{ needs.check-merged-pr.outputs.workspace_name }} on branch ${{ github.ref }} + name: LEGACY - Publish legacy workspace runs-on: ubuntu-latest needs: check-merged-pr - if: needs.check-merged-pr.outputs.is_version_pr == 'true' + if: needs.check-merged-pr.outputs.is_supported_branch == 'true' && needs.check-merged-pr.outputs.is_version_pr == 'true' && needs.check-merged-pr.outputs.is_repository_release == 'false' + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} defaults: run: working-directory: ./workspaces/${{ needs.check-merged-pr.outputs.workspace_name }} @@ -145,14 +310,14 @@ jobs: steps: - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ github.ref }} - name: Set up Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 24 - registry-url: https://registry.npmjs.org/ # Needed for auth + registry-url: https://registry.npmjs.org/ # Needed for auth - name: Install root dependencies run: yarn install --immutable @@ -163,12 +328,79 @@ jobs: run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles(format('workspaces/${0}/**/yarn.lock', needs.check-merged-pr.outputs.workspace_name)) }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: yarn install + run: yarn install --immutable + + - name: Compile TypeScript + run: yarn tsc:full + + - name: Build all packages + run: yarn build:all + + - name: publish + run: | + yarn config set -H 'npmAuthToken' "$NODE_AUTH_TOKEN" + yarn workspaces foreach -A -v --no-private npm publish --access public --tolerate-republish --tag "maintenance" + env: + NODE_AUTH_TOKEN: ${{ secrets.RHDH_NPM_TOKEN }} + + - name: Create tag + working-directory: ${{ github.workspace }}/scripts/ci + run: node create-tag.js + env: + WORKSPACE_NAME: ${{ needs.check-merged-pr.outputs.workspace_name }} + GITHUB_TOKEN: ${{ secrets.RHDH_BOT_TOKEN }} + + repository-release: + name: Publish workspace + runs-on: ubuntu-latest + needs: + - check-merged-pr + if: needs.check-merged-pr.outputs.is_supported_branch == 'true' && needs.check-merged-pr.outputs.is_version_pr == 'true' && needs.check-merged-pr.outputs.is_repository_release == 'true' + concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.base.ref }}-${{ needs.check-merged-pr.outputs.workspace_name }} + defaults: + run: + working-directory: ./workspaces/${{ needs.check-merged-pr.outputs.workspace_name }} + env: + CI: true + NODE_OPTIONS: --max-old-space-size=4096 + + steps: + - name: Checkout merged Version Packages PR + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + ref: ${{ github.event.pull_request.merge_commit_sha }} + fetch-depth: 0 + + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 24 + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: Install root dependencies + run: yarn install --immutable + working-directory: ${{ github.workspace }} + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT + + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + id: yarn-cache with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles(format('workspaces/${0}/**/yarn.lock', needs.check-merged-pr.outputs.workspace_name)) }} - restore-keys: | - ${{ runner.os }}-yarn- + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles(format('workspaces/${0}/**/yarn.lock', needs.check-merged-pr.outputs.workspace_name)) }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: yarn install run: yarn install --immutable @@ -178,17 +410,16 @@ jobs: - name: Build all packages run: yarn build:all - # CHANGED: Publish with tag "maintenance" to avoid overwriting the latest npm tag - name: publish run: | yarn config set -H 'npmAuthToken' "$NODE_AUTH_TOKEN" yarn workspaces foreach -A -v --no-private npm publish --access public --tolerate-republish --tag "maintenance" env: NODE_AUTH_TOKEN: ${{ secrets.RHDH_NPM_TOKEN }} - + - name: Create tag working-directory: ${{ github.workspace }}/scripts/ci run: node create-tag.js env: - WORKSPACE_NAME: ${{ needs.check-merged-pr.outputs.workspace_name }} - GITHUB_TOKEN: ${{ secrets.RHDH_BOT_TOKEN }} + WORKSPACE_NAME: ${{ needs.check-merged-pr.outputs.workspace_name }} + GITHUB_TOKEN: ${{ secrets.RHDH_BOT_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 800fa79251b..8ff79193fbd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,7 @@ The `redhat-developer/rhdh-plugins` repository is designed as a collaborative sp - [Dependency Updates](#dependency-updates) - [Security Fixes](#security-fixes) - [Opt-in to Knip Reports Check](#opt-in-to-knip-reports-check) + - [Opt-in to List Deprecations Check](#opt-in-to-list-deprecations-check) - [Archiving a plugin or workspace](#archiving-a-plugin-or-workspace) - [When to archive](#when-to-archive) - [Steps](#steps) @@ -121,55 +122,80 @@ A release is automatically triggered by merging the plugins “Version Packages ## Backporting patches (prior release lines) -Use this flow when you need a **new npm version** of packages in a workspace that tracks an older line than `main` (for example a security or bugfix for a release already shipped to customers). Day-to-day development and Renovate updates still happen on `main`; the `workspace/` branch is only for those maintenance releases. +Use this flow when you need a **new npm version** of packages in a workspace that tracks an older line than `main` (for example a security or bugfix for a release already shipped to customers). Day-to-day development and Renovate updates still happen on `main`; the backport branch is only for those maintenance releases. Automation for this path is defined in [`.github/workflows/release_workspace_version.yml`](.github/workflows/release_workspace_version.yml). Published packages from this workflow use the npm dist-tag **`maintenance`** so they do not replace `latest`. -### Patching an older release +There are two release flows. Choose the target branch for the release line; `workspace/{workspace}` is no longer a valid backport branch: -When patching an older release, follow the steps below to ensure the correct workflow is applied: +- **Legacy 1.10 and earlier backports** use per-workspace branches such as `release-1.10/{workspace}`. +- **2.1 and later repository-wide releases** use a single branch for the release line, such as `release-2.1`. Do not target `release-2.1/{workspace}`. -1. Verify a `workspace/${workspace}` branch exists. If not, create a `workspace/${workspace}` branch by navigating to the [branches page](https://github.com/redhat-developer/rhdh-plugins/branches) and selecting 'New branch'. - - The `${workspace}` should correspond to the specific plugin or component you are patching. +### Automated backports - The workflow requires that pull requests targeting the `workspace/${workspace}` branch be opened from a branch within the `redhat-developer/rhdh-plugins` repository. Therefore, in addition to the `workspace/${workspace}` branch, a corresponding branch must also be created (i.e. `plugin-name-x.y`). +To request a backport from `main`, comment exactly one of `/backport release-2.1`, `/backport release-1.10`, or `/backport release-1.9` on the pull request before merging. The 2.x label targets the repository-wide branch directly, such as `release-2.1`. For the legacy 1.9 and 1.10 release branches, the PR must have exactly one `workspace/` label (a PR label, not a branch); the workflow then targets the corresponding branch, such as `release-1.10/orchestrator`. The workflow creates the release label when needed, cherry-picks all commits from the source pull request into a new branch, and opens a pull request for review. If the request has multiple release labels, a legacy PR has zero or multiple workspace labels, or the cherry-pick conflicts, resolve it manually on a branch based on the target release branch and open the backport pull request. - If a branch `maintenance-changesets-release/${workspace}` already exists on the remote from a previous cycle, delete it before continuing; otherwise the Prior Version Release Workspace workflow will refuse to open a new Version Packages PR. +### Backport workflow (both branch layouts) -2. Reset the `workspace` branch from a **published** baseline: - - Reset `workspace/${workspace}` so it matches an existing **git tag** for that workspace (the tags created when releases were published), not an arbitrary commit on `main`. That way the maintenance line starts from code that was already shipped and you avoid accidentally including unreleased changes in the next npm publish. - - Browse tags in the repository to find the right release: [github.com/redhat-developer/rhdh-plugins/tags](https://github.com/redhat-developer/rhdh-plugins/tags). +The backport, Version Packages PR, and publish steps apply to both layouts. Only the backport PR target differs: -3. Apply your commits and push to a branch: +| Release line | Backport PR target | +| ------------------------- | ------------------------- | +| 1.10 and earlier (legacy) | `release-1.x/{workspace}` | +| 2.1 and later | `release-x.y` | + +For both layouts, each affected workspace gets a Version Packages branch named `maintenance-changesets-release/release-x.y/{workspace}`. + +Do not use `workspace/{workspace}` as a branch in either flow. The `release-x.y/{workspace}` target is only for legacy 1.10-and-earlier lines. + +1. Verify the appropriate release branch exists (e.g., `release-1.10/my-plugin` or `release-2.1`). If not, create it from the appropriate release tag by navigating to the [branches page](https://github.com/redhat-developer/rhdh-plugins/branches) and selecting 'New branch'. For repository-wide releases, maintainers create this branch at Feature Freeze, when the matching branch is cut in rhdh-plugin-export-overlays. + + If the corresponding `maintenance-changesets-release/...` branch already exists on the remote from a previous cycle, delete it before continuing; otherwise the Prior Version Release Workspace workflow will refuse to open a new Version Packages PR. + +2. Cherry-pick the target commit(s) from `main` and push to a branch: - Apply the necessary patch fixes or security updates. - Do not manually bump the version in `package.json`. The version bump must be handled via changesets. - - Push to a branch on the `redhat-developer/rhdh-plugins` repository. Note that it is not possible to open PRs from a fork for this release workflow. + - A changeset **must** be included in the PR to trigger a new release. -4. Open the **patch** pull request (the first PR in this flow): - - Open a pull request with your changes against the `workspace/${workspace}` branch. - - Ensure the PR: - - Contains only necessary fixes. - - Includes a changeset. + ```bash + git fetch upstream + git checkout -b backport--to- upstream/ + git cherry-pick + git push origin backport--to- + ``` -5. Merge the **patch** PR when it is approved and CI is green. +3. Open a pull request targeting the appropriate release branch from the table above and merge when approved and CI is green. - Merging this PR does **not** publish to npm by itself. It triggers the Prior Version Release Workspace workflow, which opens a **separate** follow-up pull request—the **Version Packages** PR—from branch `maintenance-changesets-release/${workspace}`, authored by `rhdh-bot`. + Merging this PR does **not** publish to npm by itself. It triggers the Prior Version Release Workspace workflow, which opens a **separate** follow-up **Version Packages** PR for each affected workspace, authored by `rhdh-bot`. For legacy branches the workspace comes from the target branch; on repository-wide branches the workflow detects affected workspaces from the merged PR. -6. Merge the corresponding **Version Packages** PR: +4. Merge the corresponding **Version Packages** PR: - The Version Packages PR must meet these conditions before you merge it: - The PR title starts with "Version Packages" (automatically generated by changesets). - - The PR originates from a `maintenance-changesets-release/${workspace}` branch. + - The PR originates from a `maintenance-changesets-release/release-x.y/{workspace}` branch. - The PR is authored by `rhdh-bot`. - The PR is merged, not just closed. - Merging **this** PR triggers the release job that builds and publishes to npm. -7. Confirm the release: +5. Confirm the release: - Once the workflow completes, a new version will be published. - A new Git tag will be created, which can be used for future patches. -8. Open a PR with the `CHANGELOG` additions to `redhat-developer/rhdh-plugins` main branch: +6. Open a PR with the `CHANGELOG` additions to `redhat-developer/rhdh-plugins` main branch: - This is necessary for history to be clear on the latest branch. - - You can use `git cherry-pick --no-commit workspace/${workspace}` and only commit the `CHANGELOG` files. + +### Yarn.lock-only changes (CVE fixes without code changes) + +When only `yarn.lock` changes (e.g., a CVE fix that bumps a transitive dependency) and no plugin code is modified, you can skip the Version Packages flow entirely — no changeset, no version bump, no npm publish is needed. + +1. Merge the `yarn.lock` fix into the applicable release branch (e.g., `release-1.10/my-plugin` for legacy releases or `release-2.1` for repository-wide releases). +2. Update `source.json` in the corresponding release branch of [rhdh-plugin-export-overlays](https://github.com/redhat-developer/rhdh-plugin-export-overlays) to point `repo-ref` to the commit with the `yarn.lock` change. +3. Run `/publish` on the overlays PR — the export step rebuilds the dynamic plugin images from source at that commit, so the CVE fix is picked up without a new npm release. + +This avoids unnecessary version bumps when no plugin API or behavior has changed. + +### Repository-wide releases from 2.1 onwards + +Starting with `release-2.1`, all workspaces in a maintenance line share one `release-x.y` branch. Follow the same [backport workflow](#backport-workflow-both-branch-layouts) above, targeting that repository-wide branch and including changesets for the affected workspaces. The workflow detects each affected workspace and opens a separate Version Packages PR for each one; merging each PR publishes that workspace with the `maintenance` npm dist-tag and creates its Git tag. ## Creating a new Workspace @@ -372,7 +398,8 @@ As a plugin owner, you are responsible for the ongoing health and maintenance of See [Keeping Workspaces Up to Date](#keeping-workspaces-up-to-date-with-backstage). - **Manage security updates and patches**: Work with your security team to address vulnerabilities according to SLA and product lifecycle requirements. - Renovate opens dependency PRs against `main`. If you must ship a fix on an older published line, follow [Backporting patches (prior release lines)](#backporting-patches-prior-release-lines) using the `workspace/` branch for that line. + Renovate opens dependency PRs against `main`. If you must ship a fix on an older published line, follow [Backporting patches (prior release lines)](#backporting-patches-prior-release-lines). Use the per-plugin `release-1.x/` flow for legacy 1.x releases and the repository-wide `release-x.y` flow for 2.1 and later. +- **Report bugs** following the [Bug Reporting Guide](docs/bug-reporting.md). - **Justify Dependency-Related PR closures**: If you choose not to merge a Renovate or dependency-related PR, include a brief explanation when closing it. @@ -416,6 +443,10 @@ Plugin owners can opt in to Knip reports check in CI by creating a `bcp.json` fi [Knip](https://knip.dev/) is a tool that helps with clean-up and maintenance by identifying unused dependencies within workspaces. Regularly reviewing and addressing these reports can significantly improve code quality and reduce bloat. +### Opt-in to List Deprecations Check + +Plugin owners can opt in to the list deprecations check in CI by creating a `bcp.json` file in the root of their workspace (`workspaces/${WORKSPACE}/bcp.json`) and adding `{ "listDeprecations": true }`. This runs `backstage-cli repo list-deprecations` against your workspace, which fails the build if any deprecated API is still being used, helping you catch and remove usages of deprecated APIs before they become a problem. + ## Archiving a plugin or workspace When a plugin is no longer maintained, archive it rather than leaving stale code in the default branch. The archive script records the last published version in `.github/archived-plugins.json`, documents it in `ARCHIVED_WORKSPACES.md`, and strips repository configuration that referenced the workspace when you archive an entire workspace. You then remove the workspace or plugin tree in the same PR (or a follow-up). After the PR merges, npm deprecation runs via `.github/workflows/deprecate-archived-plugins.yml`. @@ -444,6 +475,8 @@ Consider archiving when the plugin is unmaintained, has no owner, has unfixable - `ARCHIVED_WORKSPACES.md` - `.github/CODEOWNERS` (full workspace only — removes the `/workspaces/` line) - `.github/renovate.json` and `.github/renovate-presets/workspace/rhdh--presets.json` (full workspace only, when present) + - `.github/labeler.yml` and `.github/pr-labeler.yml` (full workspace only, removes the workspace's entry) + - `codecov.yml` (full workspace only, removes the workspace's entry) It does **not** delete source directories; do that in the next step.