npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@cleyrop-org/semantic-release-backmerge

v5.2.4

Published

semantic-release plugin to back-merge releases into development branches. Fork of @saithodev/semantic-release-backmerge by Mario Lubenka, maintained by Cleyrop Organization.

Downloads

91

Readme

@cleyrop-org/semantic-release-backmerge

Originally created by Mario Lubenka (@saithodev) Maintained and enhanced by Jean Humann for Cleyrop Organization

semantic-release plugin to back-merge releases into development branches in your Git repository.

semantic-release Commitizen friendly NPM Version NPM Downloads

What is this plugin?

This plugin enables automated back-merging from production/release branches (e.g., main/master) to development branches (e.g., develop/next) as part of your semantic-release workflow. This is particularly useful for teams using Git Flow or similar branching strategies.

When to use this plugin

  • You maintain separate production and development branches
  • You want to automatically sync releases back to your development branch
  • You use semantic-release for automated versioning and releases
  • You need to maintain development branch consistency after releases

Important Note

semantic-release in its core is not designed for Git Flow workflows with stable and unstable branches. This plugin enables such workflows, but does not guarantee seamless operation in all scenarios.

⚠️ Hotfix releases may cause merge conflicts with the development branch that require manual resolution. In such cases, the release workflow will fail, causing CI/CD pipeline failures.

| Step | Description | | ------------------ | ---------------------------------------------------------------------------------------------- | | verifyConditions | Verify access to the remote Git repository and validate the backmergeBranches configuration. | | done | Create a back-merge into the configured branch(es) if the release is successful. |

Installation

npm install @cleyrop-org/semantic-release-backmerge --save-dev

Usage

Configure the plugin in your semantic-release configuration file:

Important: This plugin rebases or merges your development branch with your production branch. Ensure you have no unstaged files in your workspace, or use the clearWorkspace option to stash them temporarily.

Basic Configuration

{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@cleyrop-org/semantic-release-backmerge",
      {
        "backmergeBranches": ["develop"],
        "backmergeStrategy": "rebase"
      }
    ]
  ]
}

Cascading Backmerge Configuration

Create multi-level backmerge chains (e.g., mainstagingdevelop):

{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@cleyrop-org/semantic-release-backmerge",
      {
        "backmergeBranches": [
          { "from": "main", "to": "staging" },
          { "from": "staging", "to": "develop" }
        ],
        "backmergeStrategy": "merge"
      }
    ]
  ]
}

When a release is made from main, this configuration will:

  1. Backmerge mainstaging
  2. Backmerge stagingdevelop (cascading from previous step)

Advanced Configuration with Plugins

You can run additional plugins during the backmerge to modify files for your development branch:

{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@cleyrop-org/semantic-release-backmerge",
      {
        "backmergeBranches": ["develop"],
        "plugins": [
          [
            "@semantic-release/exec",
            {
              "successCmd": "echo 'Version in main is ${nextRelease.version}' > VERSION.txt && git add VERSION.txt"
            }
          ]
        ]
      }
    ]
  ]
}

CI/CD Platform Examples

GitLab CI

release:
  stage: release
  image: node:20
  script:
    - npm ci
    - npx semantic-release
  only:
    - main

GitHub Actions

- uses: actions/checkout@v4
  with:
    fetch-depth: 0
    persist-credentials: false # Required for backmerging into protected branches

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: "20"

- name: Install dependencies
  run: npm ci

- name: Release
  env:
    GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} # Use PAT with repo scope
  run: npx semantic-release

Note for GitHub Actions: To backmerge into protected branches, you need:

  1. Disable persist-credentials in the checkout action
  2. Use a Personal Access Token with repo scope in GITHUB_TOKEN
  3. Ensure the token owner has admin/maintainer access to the repository

Jenkins

withCredentials([usernamePassword(credentialsId: JENKINS_GIT_CREDENTIALS_ID,
                                  passwordVariable: 'GIT_PASSWORD',
                                  usernameVariable: 'GIT_USERNAME')]) {
    sh("git config credential.username ${GIT_USERNAME}")
    sh("git config credential.helper '!f() { echo password=$GIT_PASSWORD; }; f'")
}
withCredentials([usernameColonPassword(credentialsId: JENKINS_GIT_CREDENTIALS_ID,
                                        variable: 'GIT_CREDENTIALS')]) {
    nodejs(JENKINS_NODE_JS_INSTALLATION_LABEL) {
        sh("npx semantic-release")
    }
}

Configuration Options

Core Options

| Option | Description | Default | | ---------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------- | | backmergeBranches | Branch names to receive the back-merge. See details. | ['develop'] | | backmergeStrategy | How to perform the backmerge: rebase or merge. See details. | rebase | | message | Commit message for back-merge (if files changed). See details. | chore(release): Preparations for next release [skip ci] | | forcePush | Force-push the back-merge. See details. | false | | allowSameBranchMerge | Allow back-merging into the same branch as release source. | false |

Workspace Options

| Option | Description | Default | | ------------------ | --------------------------------------------------------------------------- | ------- | | clearWorkspace | Stash uncommitted changes before backmerge. See details. | false | | restoreWorkspace | Restore stashed changes after backmerge. See details. | false |

Merge Strategy Options

| Option | Description | Default | | ----------------- | --------------------------------------------------------------------------------- | ------- | | mergeMode | Conflict resolution when using merge strategy. See details. | none | | fastForwardMode | Fast-forward option when using merge strategy. See details. | none |

Plugin Options

| Option | Description | Default | | --------- | -------------------------------------------------------------------- | ------- | | plugins | Additional plugins to run during backmerge. See details. | [] |

Performance Options

| Option | Description | Default | | ------------ | ---------------------------------------------------------------------------------- | ------- | | fetchDelay | Delay (in ms) after fetch to allow remote git provider to sync. See details. | 3000 |


Option Details

backmergeBranches

Specify which branches should receive the back-merge. Accepts:

  • String array: Simple branch names
  • Object array: Conditional or cascading backmerges with {from: "source", to: "target"} format

Examples

Simple backmerge to develop:

{
  "backmergeBranches": ["develop"]
}

Multiple branches:

{
  "backmergeBranches": ["develop", "next"]
}

Conditional backmerges:

{
  "backmergeBranches": [
    "develop",
    { "from": "main", "to": "staging" },
    { "from": "next", "to": "beta" }
  ]
}

In the conditional example:

  • All releases back-merge to develop
  • Releases from main also back-merge to staging
  • Releases from next also back-merge to beta

Cascading backmerges:

{
  "backmergeBranches": [
    { "from": "main", "to": "staging" },
    { "from": "staging", "to": "develop" }
  ]
}

In the cascading example, when a release is made from main:

  1. First, main is backmerged into staging
  2. Then, staging is backmerged into develop

This creates a cascade: mainstagingdevelop

How cascading works:

The plugin tracks which branches have been backmerged to during the current release workflow. When it encounters a {from: X, to: Y} configuration:

  • Conditional backmerge: If from equals the release branch OR has not been backmerged to yet, it only proceeds if the release originated from that branch
  • Cascading backmerge: If from is a branch that was already a to target in a previous backmerge, it will merge from that branch regardless of the release origin

This allows you to create multi-level backmerge chains while maintaining backward compatibility with existing configurations.

Mixed configuration example:

{
  "backmergeBranches": [
    "hotfix",
    { "from": "main", "to": "staging" },
    { "from": "staging", "to": "develop" }
  ]
}

When a release is made from main:

  • mainhotfix (simple backmerge)
  • mainstaging (conditional backmerge)
  • stagingdevelop (cascading backmerge)

Template Variables

You can use Lodash template syntax in branch names:

{
  "backmergeBranches": ["${branch.name}-develop"]
}

Available variables:

| Variable | Description | | ------------------- | -------------------------------------------- | | branch | The release branch object | | branch.name | Branch name | | branch.type | Branch type (release/prerelease/maintenance) | | branch.channel | Distribution channel | | branch.range | Semantic version range | | branch.prerelease | Prerelease identifier |

backmergeStrategy

Determines how the development branch receives changes from the production branch.

Options:

  • rebase (default): Replays development branch commits on top of production
  • merge: Creates a merge commit from production into development

Example:

{
  "backmergeStrategy": "merge"
}

plugins

Run additional semantic-release plugins during the backmerge phase. Useful for modifying files for the development branch (e.g., updating version files, adding dev suffixes).

Important: Plugins must stage their changes to Git (git add) for inclusion in the back-merge commit.

Example:

{
  "plugins": [
    [
      "@semantic-release/exec",
      {
        "successCmd": "npm version ${nextRelease.version}-dev --no-git-tag-version && git add package.json"
      }
    ]
  ]
}

message

Customize the commit message for back-merge commits using Lodash template syntax.

Available variables:

| Variable | Description | | ------------- | --------------------------------------------------------- | | branch | Release branch object (see structure above) | | lastRelease | Previous release (version, gitTag, gitHead) | | nextRelease | Current release (version, gitTag, gitHead, notes) |

Example:

{
  "message": "chore: merge v${nextRelease.version} into develop [skip ci]"
}

Tip: Include [skip ci] to prevent triggering another CI build.

forcePush

Force-push the back-merge to the development branch.

⚠️ Warning: This will overwrite commits in the development branch that aren't in production. Use with extreme caution!

{
  "forcePush": true
}

clearWorkspace

Stash uncommitted changes before attempting the backmerge.

{
  "clearWorkspace": true
}

restoreWorkspace

Restore stashed changes after the backmerge completes. Only meaningful with clearWorkspace: true.

{
  "clearWorkspace": true,
  "restoreWorkspace": true
}

mergeMode

When using backmergeStrategy: "merge", determines conflict resolution strategy.

Options:

  • none (default): No automatic resolution (fails on conflicts)
  • ours: Prefer development branch changes
  • theirs: Prefer production branch changes
{
  "backmergeStrategy": "merge",
  "mergeMode": "theirs"
}

fastForwardMode

When using backmergeStrategy: "merge", controls fast-forward behavior.

Options:

  • none (default): Same as ff
  • ff: Fast-forward when possible, merge commit otherwise
  • no-ff: Always create a merge commit
  • ff-only: Only fast-forward, fail if not possible
{
  "backmergeStrategy": "merge",
  "fastForwardMode": "no-ff"
}

fetchDelay

Delay (in milliseconds) after pushing to the remote repository before fetching to update local branch references. This allows remote git providers (GitHub, GitLab, Bitbucket) additional time to fully process and synchronize the push operation.

Why you might need this:

  • Remote git providers may have a small delay between when a push completes and when those changes are available for subsequent fetch operations
  • This is critical for cascading backmerges where one backmerge's push must be fully visible to the remote before the next backmerge fetches
  • Network latency, CDN propagation, and API processing can cause timing issues
  • Without this delay, cascading backmerges may not include all expected commits

Default: 3000 (3 seconds)

Examples:

Disable delay for local repositories or faster CI:

{
  "fetchDelay": 0
}

Increase delay for slower networks or remote providers:

{
  "fetchDelay": 5000
}

Note: The default 3-second delay provides a reasonable buffer for most remote git providers. Only adjust if you experience timing-related failures or want to optimize CI speed for local repositories.

Protected Branches

To backmerge into protected branches:

  1. Use credentials with admin/maintainer permissions
  2. Provide appropriate access token (e.g., GITHUB_TOKEN, GITLAB_TOKEN)
  3. For GitHub Actions, disable persist-credentials in checkout action

Troubleshooting

Merge Conflicts

If automated backmerge fails due to conflicts:

  1. Manually resolve conflicts in your development branch
  2. The next release will attempt backmerge again

Stale Workspace Issues

If you encounter "unstaged changes" errors:

  • Set clearWorkspace: true to automatically stash changes
  • Set restoreWorkspace: true if you need those changes restored

CI/CD Pipeline Loops

Ensure your commit message includes [skip ci] or equivalent to prevent infinite release loops.

Cascading Backmerge Issues

If cascading backmerges (e.g., mainstagingdevelop) are not including all expected commits:

Symptoms:

  • Commits from the first branch (e.g., main) don't appear in the final branch (e.g., develop)
  • Merge messages reference the correct branch but history is incomplete
  • Integration tests show missing commits in cascade chains

Solutions:

  1. Increase fetchDelay: The default 3-second delay may not be sufficient for some remote providers

    {
      "fetchDelay": 5000
    }
  2. For local testing: Set fetchDelay: 0 since local git repositories don't have synchronization delays

    {
      "fetchDelay": 0
    }
  3. Check remote configuration: Ensure your git remote is properly configured as origin

    git remote -v

Technical Note: The plugin uses git fetch origin to update remote-tracking branches. If your remote has a different name, you may need to adjust your git configuration or create an origin alias.

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see the LICENSE file for details

Acknowledgments

This project is a fork and continuation of the original @saithodev/semantic-release-backmerge created by Mario Lubenka. We're grateful for the foundational work and maintain this fork to provide continued support and enhancements.

Links