Skip to content

Backport

ernolf edited this page Oct 24, 2026 · 1 revision

🍒 The backport command

A fix merged into main usually has to reach the maintenance lines too. By hand that is a cherry-pick, a branch, a push and a pull request per fix and per line, and a cherry-pick committed in a terminal or a runner without your key is not verified. The backport workflow does it from a comment on the pull request.

Tip

TL;DR: Install with make workflows-install W=backport, with the GitHub App of the workflow updater set up. Then write /backport to stable0.7 as a comment on a pull request, and once it is merged a verified, signed-off pull request against stable0.7 opens for you.

🧩 Installing it

backport.yml is a template of the ncmake source, so it installs through the workflow manager:

make workflows-install W=backport
git add .github/workflows/

Merge it to the default branch. A comment always runs the copy there. The merge runs the copy on the branch the pull request was merged into, so a command that waits on an open pull request against a stable* branch runs only if that line has the workflow too; without it, write the command once the pull request is merged.

It authenticates with the same GitHub App as the workflow updater, through the NCMAKE_UPDATER_CLIENT_ID and NCMAKE_UPDATER_PRIVATE_KEY secrets. The app token is what makes the backport commit verified, what lets it carry a change to a workflow file, and what makes the backport pull request run your CI checks.

💬 The command

Write it as a comment on the pull request, on a line of its own:

/backport to stable0.7

This is the syntax of the Nextcloud backport bot, so it is familiar to anyone who works on Nextcloud apps. One line per branch backports to several lines at once:

/backport to stable0.7
/backport to stable0.6
  • On a merged pull request the backport runs right away.
  • On an open pull request the command waits for the merge. The merge reads every comment of the pull request again and runs each command it finds. When several comments name the same branch, the newest one counts.
  • Who may ask: only a comment whose author association is OWNER, MEMBER or COLLABORATOR. Anyone else gets an answer saying so, and nothing runs.

🚀 The backport pull request

  • Content: the commits of the pull request, cherry-picked onto the target branch. They come from refs/pull/<number>/head, so they are the original commits whichever way the pull request was merged; merge commits inside it are left out, as the Nextcloud bot does.
  • One commit: the cherry-pick runs with --no-commit, and create-pull-request commits the result through the API. The commit is signed by the app's bot, so it is Verified, and carries its Signed-off-by, so DCO passes.
  • Commit message: the title of the backport, then the message of every original commit, sign-offs included, each followed by the (cherry picked from commit …) line git cherry-pick -x adds.
  • Branch: ncmake/backport/<number>/<target branch>, for example ncmake/backport/42/stable0.7.
  • Title: the original title with the target branch appended, fix: keep the label [stable0.7].
  • Body: a link to the original pull request and the list of commits.

Review and merge it like any other pull request against the line. Its branch goes away after the merge through Automatically delete head branches or the branch-cleanup workflow.

🧱 When it cannot backport

The job never leaves a half-done branch behind. Instead it answers on the original pull request:

Case Answer
the change conflicts with the target branch the conflicting files and the commands for a manual backport
the target branch does not exist the name it did not find
the commenter may not ask who may
the command is not understood the syntax
the change is already on the target branch that there is nothing to backport
the pull request was closed without a merge that there is nothing to backport

The manual backport for a conflict is the flow from Releasing: fetch the branch and the pull request, cherry-pick with -x, resolve, push and open the pull request yourself.

😀 Reactions

The comment with the command shows how far it got:

Reaction Meaning
👀 picked up; on an open pull request it now waits for the merge
🚀 the backport pull request is open
👎 the backport failed, the answer says why
😕 the command was not understood

🔐 Why the merge trigger is pull_request_target

A merge starts the backport through the closed event of the pull request. A pull_request workflow runs without the repository's secrets when the pull request comes from a fork, so the app token could not be minted for an outside contribution. pull_request_target runs in the context of the repository instead.

That trigger is safe here because no code of the pull request runs: the job checks out the target branch, fetches the commits of the pull request and cherry-picks them, which only applies their changes. The backport pull request then runs your CI like any other pull request.

Clone this wiki locally