Skip to content

feat: add release-beta-lock action [ED-25903] - #63

Open
davseve wants to merge 2 commits into
mainfrom
feat/ED-25903-release-beta-lock
Open

davseve wants to merge 2 commits into
mainfrom
feat/ED-25903-release-beta-lock

Conversation

@davseve

@davseve davseve commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds a release-beta-lock composite action that core and Pro can use to lock or unlock release/beta for merges as part of the release cycle.

  • state: locked turns on a dedicated repository ruleset named Release beta lock. Its only rule is update, so nobody without bypass permission can push or merge into the branch.
  • state: unlocked turns that ruleset off.
  • The ruleset is created on the first run. The existing "main and releases" ruleset is not touched.
  • API failures emit an ::error annotation that names the failed operation and fail the step, so a missing lock is never silent.

Planned callers (core PR follows this one):

  • Unlock: at branch-out (create-version-change.yml), before any branch work.
  • Lock: after publishing the first stable release of a minor (X.Y.0) in controlled-release.yml.

This lines up with sync-release-branches: once stable reaches the beta's X.Y, branch sync already treats beta as inactive.

Token

Managing rulesets needs repository Administration: write, which GITHUB_TOKEN can't be granted. Callers pass MAINTAIN_TOKEN for now, so its owner must be a repo admin.

Follow-up: replace MAINTAIN_TOKEN with a GitHub App token (actions/create-github-app-token) installed on core and Pro. That way the lock isn't tied to a personal account, and the app can be added as a bypass actor if needed.

Test plan

  • bash actions/release-beta-lock/release-beta-lock.test.sh: 16 tests with a stubbed gh (create, update, invalid state, list/update failures)
  • Ruleset lookup (env.RULESET_NAME in --jq) checked against the real elementor/elementor rulesets API
  • prettier --check
  • First real run from a core workflow

Jira

ED-25903

Made with Cursor

✨ PR Description

1. Problem & Context

Implements a GitHub Action (ED-25903) to programmatically lock or unlock the beta release branch using repository rulesets to prevent unauthorized merges.

2. What Changed (Where)

  • actions/release-beta-lock/release-beta-lock.sh: Bash script to manage ruleset enforcement via GH API.
  • actions/release-beta-lock/action.yml: Composite action definition with inputs for token, state, and branch.
  • actions/release-beta-lock/package.json: Metadata and test script definition.
  • actions/release-beta-lock/release-beta-lock.test.sh: Comprehensive test suite mocking GH API calls.

3. How It Works

The action takes a target state (locked/unlocked) and branch, then checks for an existing repository ruleset by name. If found, it updates the enforcement status via PUT; otherwise, it creates a new ruleset via POST with an "update" rule targeting the specified branch.

4. Risks

Requires a PAT or GitHub App token with Administrative write permissions, as the default GITHUB_TOKEN cannot modify repository rulesets.

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Description using Guidelines Learn how

Lock or unlock release/beta for merges by toggling a dedicated
"Release beta lock" ruleset that holds an "update" rule. The ruleset is
created on first run. API failures emit an error annotation and fail the step.

Ref: ED-25903
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread actions/release-beta-lock/action.yml Fixed
- Fix lockfile: regenerate package-lock.json with npm install to include @elementor/release-beta-lock
- Fix CodeQL code injection: pass github.action_path through env var instead of inline interpolation
- Fix SIGPIPE: capture gh output first, then take first line to avoid pipeline failure
- Fix org-level rulesets: filter for source_type == 'Repository' to exclude org rulesets
- Fix test isolation: run tests in clean subshells, prevent global variable leakage
- Add new tests: default branch, missing repo, disabled enforcement, org filtering, duplicates
- Fix error propagation: explicitly return gh failure code in find_ruleset_id

All tests pass locally. Resolves review items 1-5.

Co-authored-by: Davids Seveloff <davseve@users.noreply.github.com>
davseve added a commit to elementor/elementor that referenced this pull request Oct 26, 2026
… branch [ED-25980] (#37834)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## PR Checklist
- [x] The commit message follows our guidelines

## PR Type
- [x] CI related changes

## Summary

This PR can be summarized in the following changelog entry:

* Changes the `Create Version Change` workflow and script to reset
`release/beta` to `main` instead of cutting a new X.YY branch (e.g.,
4.04), so `release/beta` always holds the current version and `main`
holds the next minor version after the bump PR merges.


## Visual proof

#skip_proof
CI-only change to the Create Version Change workflow and script; nothing
to verify in the Elementor editor on Playground.

## Description

The `create-version-change` workflow and script now:

1. **Stops creating X.YY branches** — the workflow no longer cuts a new
numeric branch from main
2. **Resets `release/beta` to `origin/main`** BEFORE bumping versions on
main, using `--force-with-lease` for safety
3. **Implements a patch-equivalence guard** using `git log --cherry-pick
--right-only --no-merges` to detect unique commits on `release/beta`.
Fails the workflow if found (unless `force_beta_reset: true` is passed,
which downgrades to a warning)
4. **Renames `skip_branches` → `skip_beta_reset`** (keeps
`-s/--skip-branches` as a deprecated CLI alias)
- `skip_beta_reset` skips ONLY the reset — version bump and PR creation
still happen
5. **Adds `force_beta_reset` input** (boolean, default false) to
override the guard check
6. **Runs the guard in dry_run mode** (read-only checks)
7. **Warns about open PRs** targeting `release/beta` (non-fatal,
requires `gh` CLI)
8. **Removes unused `branchName` variable** (X.YY branch calculation no
longer needed)

**Result:** After this change, `release/beta` will hold 4.4.0, and
`main` will hold 4.5.0 (after the version bump PR merges). No more X.YY
branches.

### Workflow Behavior

| Scenario | Reset release/beta? | Create dev branch? | Version bump? |
Workflow commit/push/PR? |

|----------|--------------------|--------------------|---------------|--------------------------|
| Default run | ✅ Yes (with guard) | ✅ Yes (from origin/main) | ✅ Yes |
✅ Yes |
| `skip_beta_reset: true` | ❌ No | ✅ Yes (from origin/main) | ✅ Yes | ✅
Yes |
| `dry_run: true` | Read-only guard | ✅ Yes (local) | ✅ Yes (local) | ❌
No |
| `force_beta_reset: true` | ✅ Yes (guard → warning) | ✅ Yes (from
origin/main) | ✅ Yes | ✅ Yes |

## Current State Analysis

Against the current repository state:

```bash
git log --cherry-pick --right-only --no-merges --format='%h %s' origin/main...origin/release/beta
```

**The guard currently flags 5 unique commits on `release/beta`:**

1. `3b35fb24b1` — Internal: Bump wp-one-package to 1.0.71 [ED-25626]
(#37406)
2. `f2fd9fc59d` — Internal: Cherry-pick PR 37308 to release/beta with
conflicts: Add per-tool MCP performance monitoring events [ED-25572]
(#37315)
3. `9778977c90` — Internal: Cherry-pick to release/beta: Exclude
components from import/export until full support [ED-25468] (#37201)
4. `db92a2d6aa` — Fix: Cherry-pick PR 37145 to release/beta: Wrap
compound atomic elements in flexbox on canvas drop [ED-25233] (#37170)
5. `7cdbb9e4f0` — Internal: Cherry-pick PR 37092 to release/beta with
conflicts: Open Angie in Ask mode when Help Center is clicked [ED-25421]
(#37138)

These are cherry-pick PRs whose content is already in main
(squash-merged). The simple `rev-list` check would have failed
(release/beta is 77 commits ahead by SHA), but the cherry-pick
equivalence check correctly identifies only 5 unique commits.

**A real workflow run today will fail without `force_beta_reset: true`**
until these commits are resolved or the workflow is invoked with the
force flag.

**Open PRs targeting `release/beta` (will become stale after reset):**
- #37831: Fix: Cherry-pick PR 37821 to release/beta: Skip repeated
atomic style passes without new posts [ED-25487]

## Dependencies on X.YY branches

Workflows that include `'[0-9].[0-9][0-9]'` or `'[0-9][0-9].[0-9][0-9]'`
branch patterns (no action needed in this PR — these patterns will
simply not match once X.YY branches are no longer created):

### In `branches:` filters (push/pull_request triggers):
- `.github/workflows/pr.yml` — line 10: `- '[0-9].[0-9][0-9]'`
- `.github/workflows/latest-release.yml` — lines 14-15: `-
'[0-9].[0-9][0-9]'`, `- '[0-9][0-9].[0-9][0-9]'`
- `.github/workflows/build.yml` — line 22: `- '[0-9].[0-9][0-9]'`
- `.github/workflows/visual-proof-author.yml` — line 10: `-
'[0-9].[0-9][0-9]'`
- `.github/workflows/lighthouse.yml` — line 12: `- '[0-9].[0-9][0-9]'`
- `.github/workflows/pr-cancel-superseded.yml` — line 10: `-
'[0-9].[0-9][0-9]'`
- `.github/workflows/css-file-check.yml` — line 9: `-
'[0-9].[0-9][0-9]'`
- `.github/workflows/performance-check.yml` — line 9: `-
'[0-9].[0-9][0-9]'`

### In manual workflow_dispatch branch input options:
- `.github/workflows/latest-release.yml` — branch input includes options
for `main`, `release/beta`, `release/stable`, and `other` (no numeric
branch in the options)

**Analysis:** These workflows are triggered by pushes/PRs to `main`,
`release/beta`, `release/stable`, **OR** X.YY branches. After this
change, X.YY branches will no longer be created, so those triggers will
simply not fire for numeric branches (which is the intended behavior).
No workflow changes are required.

## Permissions

The workflow uses `secrets.MAINTAIN_TOKEN` for:
- Fetching `origin/main` and `origin/release/beta`
- Force-pushing to `release/beta` with `--force-with-lease`
- Creating the version bump branch and PR

**Requirements:** The token's identity (the
`MAINTAIN_USERNAME`/`MAINTAIN_EMAIL` GitHub user) must:
1. Have write access to the repository
2. Be allowed to force-push to `release/beta` (bypass branch protection
if enabled)
3. Be exempt from or satisfy any `lock_branch` (branch lock) rules on
`release/beta`

## Interaction with ED-25903 / #37787

**Merge order:**
1. `elementor/elementor-editor-github-actions#63` (adds `unlock_branch`
action)
2. #37787 (ED-25903: adds unlock step to `create-version-change.yml`
gated on `skip_branches`)
3. **This PR** (ED-25980: resets `release/beta` instead of cutting X.YY)

**On rebase:** After #37787 merges, the unlock step it adds must be:
- **Moved after the guard checks and before the reset** (so
`release/beta` is unlocked before `--force-with-lease` pushes to it)
- **Condition renamed from `skip_branches` to `skip_beta_reset`**
(aligned with this PR's renamed input)
- **Kept unlocked after the reset** (no re-lock after success)
- **Only re-locked on guard failure** (if the guard blocks the reset,
the unlock must be reverted)

ED-25903 will need a small follow-up adjustment after this PR merges to
align the unlock step placement and condition.

## Test instructions

This PR can be tested by:

1. **Dry run mode:**
   ```bash
   node scripts/create-version-change.js 4.5.0 --dry-run
   ```
Expected: Fetches branches, runs guard, prints the `--force-with-lease`
command without executing it, and prints version bump commands without
changes.

2. **Guard passes (no unique commits):**
Create a test repo where `release/beta` and `main` are at the same
commit, run the script → guard passes, reset proceeds.

3. **Guard fails (unique commits):**
Add a commit to `release/beta`, run the script → guard detects commits,
fails with error + commit log.

4. **Force reset (override guard):**
Run with `--force-beta-reset` → guard detects commits, prints warning,
proceeds with reset.

5. **Skip beta reset:**
Run with `--skip-beta-reset` (or deprecated `-s/--skip-branches`) →
skips reset section, still creates dev branch from origin/main, version
bump still happens, workflow PR steps still run.

6. **Open PRs warning:**
If `gh` CLI is available and there are open PRs targeting
`release/beta`, the script prints a warning listing them (non-fatal).

## Verification Results

All 4 scenarios verified in scratch git repo:

```
=== Scenario (a): Default run - resets beta then creates dev branch from origin/main ===
✓ PASS: No unique commits, guard would pass
✓ Would reset release/beta to origin/main with --force-with-lease
✓ Would create version-4.5.0-to-main from origin/main
✓ Workflow commit/push/PR steps would run (dry_run != 'true')

=== Scenario (b): skip_beta_reset - no reset, dev branch still created, workflow runs ===
With --skip-beta-reset:
✓ Would skip fetching release/beta
✓ Would skip guard check
✓ Would skip reset command
✓ Would still fetch origin/main
✓ Would still create version-4.5.0-to-main from origin/main
✓ Workflow commit/push/PR steps would still run (only gated on dry_run)

=== Scenario (c): dry_run - guard runs, reset printed, no push ===
With --dry-run:
✓ Would fetch origin/main and origin/release/beta
✓ Would run guard check (read-only)
✓ Would print: DRY RUN: git push --force-with-lease=release/beta:abc123... origin origin/main:refs/heads/release/beta
✓ Would create dev branch but NOT commit/push (workflow steps gated on dry_run != 'true')

=== Scenario (d): Guard with unique commits ===
✓ PASS: Guard would detect 1 unique commit(s):
7715854 Extra commit on release/beta
✓ Would fail without --force-beta-reset
✓ With --force-beta-reset: would warn and proceed
```

## Quality assurance

- [x] I have tested this code to the best of my abilities
- [ ] I have added unittests to verify the code works as intended (no
test suite exists for this script)
- [x] Manual verification completed in scratch git repo for all
scenarios

## Changes

### Commit 1 (`0503ea7266`): Initial implementation
- Renamed `skip_branches` → `skip_beta_reset` in workflow and script
- Added `force_beta_reset` input
- Implemented patch-equivalence guard with cherry-pick check
- Reset `release/beta` to `origin/main` with `--force-with-lease`
- Added open PR warning (requires `gh` CLI)
- Removed broken branch-exists check

### Commit 2 (`2d1ebc028c`): Fix skip_beta_reset behavior
- Fixed workflow gates: commit/push/PR steps now only gated on `dry_run`
(not `skip_beta_reset`)
- Moved dev branch creation outside `skipBetaReset` block (always
creates from `origin/main`)
- Fetch `origin/main` in all paths; `origin/release/beta` only when not
skipping reset
- Removed unused `branchName` variable and `branch_name` workflow output
- Made final script message consistent for all code paths
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-42b097a5-ccd6-59c2-930b-9b16c37ebe7a?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-42b097a5-ccd6-59c2-930b-9b16c37ebe7a&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



[ED-25626]:
https://elementor.atlassian.net/browse/ED-25626?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Davids Seveloff <davseve@users.noreply.github.com>
Co-authored-by: Svitlana-Dykun <svitlanad@elementor.red>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants