diff --git a/debian/changelog b/debian/changelog
index d1ed623819733e4996314dce2948368445919c57..89e912501884bb38363eec0fd2e6961bbe966056 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,29 @@
+apertis-dev-tools (0.2026.1) apertis; urgency=medium
+
+  * import-debian-package: stop using a PosixPath object as context manager.
+    This is no longer supported since Python 3.13 and triggers an error.
+
+ -- Dylan Aïssi <dylan.aissi@collabora.com>  Tue, 25 Feb 2025 09:47:11 +0100
+
+apertis-dev-tools (0.2026.0) apertis; urgency=medium
+
+  * apertis-pkg-merge-upstream-to-downstreams:
+      - Don't print useless information while canceling pipelines.
+      - ensure merge request is created against the right project:
+        In case of a forked project, like we are doing with the test of the
+        rebase, the GitLab API creates by default the MR against the original
+        project instead of the forked project. To ensure the merge request is
+        created against the forked project, we use the push option
+        merge_request.target_project to overwrite the default behavior and to
+        create the merge request against the forked project.
+  * Use upstream recommended workaround to still return a RunningCommand object
+    of an executed sh command. This is required due to a breaking change in
+    python3-sh 2.x.
+  * Keep compatibility with python3-sh 1.x, by using _return_cmd
+    only when required.
+
+ -- Walter Lozano <walter.lozano@collabora.com>  Wed, 19 Feb 2025 13:55:39 +0100
+
 apertis-dev-tools (0.2024.19) apertis; urgency=medium
 
   * ci-license-scan: Add proposed whitelist.
diff --git a/tools/apertis-pkg-merge-updates b/tools/apertis-pkg-merge-updates
index 93667b53ec837843b7f664e1cb8daea106d476ba..78070a6193518ecbd8b61ee78dd0e83dbad93843 100755
--- a/tools/apertis-pkg-merge-updates
+++ b/tools/apertis-pkg-merge-updates
@@ -15,10 +15,12 @@ from pathlib import Path
 
 from debian.changelog import Changelog, VersionError, format_date
 from debian.debian_support import Version
+from sh import __version__ as sh_version
 from sh.contrib import git
 
 APERTIS_CI_NAME = "Apertis CI"
 APERTIS_CI_EMAIL = "devel@lists.apertis.org"
+RETURN_CMD = {} if sh_version.startswith("1.") else {"_return_cmd": True}
 
 
 def git_dir() -> str:
@@ -176,6 +178,7 @@ def main():
                 "merge",
                 "--ff-only",
                 rebase_tip,
+                **RETURN_CMD,
                 _ok_code=[0, 128],
                 _out="/dev/stdout",
                 _err="/dev/stderr",
@@ -215,6 +218,7 @@ def main():
         args.upstream,
         ":!debian/changelog",
         ":!debian/apertis/*",
+        **RETURN_CMD,
         _ok_code=[0, 1],
     )
     msg = [f"Sync from {args.upstream}."]
diff --git a/tools/apertis-pkg-merge-upstream-to-downstreams b/tools/apertis-pkg-merge-upstream-to-downstreams
index aca0aea9fdd2c7217820a9117b0e0e86deb5249d..8927cdb865033d5a36bfd5367d45ad8566df3ba1 100755
--- a/tools/apertis-pkg-merge-upstream-to-downstreams
+++ b/tools/apertis-pkg-merge-upstream-to-downstreams
@@ -16,9 +16,13 @@ import urllib.parse
 import urllib.request
 from functools import partial
 
-from sh import ErrorReturnCode, ErrorReturnCode_2, apertis_pkg_merge_updates, echo
+from sh import ErrorReturnCode, ErrorReturnCode_2
+from sh import __version__ as sh_version
+from sh import apertis_pkg_merge_updates, echo
 from sh.contrib import git
 
+RETURN_CMD = {} if sh_version.startswith("1.") else {"_return_cmd": True}
+
 
 @dataclasses.dataclass
 class Conflict:
@@ -91,6 +95,7 @@ def is_minor_change(upstream, downstream):
         downstream,
         ":!debian/changelog",
         ":!debian/apertis/*",
+        **RETURN_CMD,
         _ok_code=[0, 1],
     )
     return o.exit_code == 0
@@ -117,7 +122,6 @@ def cancel_branch_context_pipeline(project_id, branch, token, project_url):
     try:
         res = urllib.request.urlopen(get_id_req)
         json_res = json.load(res)
-        print(json_res)
         latest_job_id = json_res["id"]
     except urllib.error.HTTPError as e:
         print("ERROR:", e.read().decode())
@@ -136,7 +140,6 @@ def cancel_branch_context_pipeline(project_id, branch, token, project_url):
     print(f"Canceling useless pipeline on {branch}")
     try:
         res = urllib.request.urlopen(cancel_req).read().decode("utf-8")
-        print(res)
     except urllib.error.HTTPError as e:
         print("ERROR:", e.read().decode())
 
@@ -185,6 +188,32 @@ def ensure_downstream_branch(project_url, downstream, dry_run):
         raise
 
 
+def get_path_with_namespace(project_url):
+    url = urllib.parse.urlsplit(project_url)
+    project_id = urllib.parse.quote(removesuffix(url.path.strip("/"), ".git"), safe="")
+    # drop the inline auth data as urllib does not like it
+    auth, netloc_no_auth = url.netloc.split("@", 1)
+    token = auth.split(":", 1)[-1]
+    url = url._replace(
+        path=f"/api/v4/projects/{project_id}",
+        netloc=netloc_no_auth,
+    )
+    project_metadata = url.geturl()
+    req_project_metadata = urllib.request.Request(
+        url=project_metadata,
+        headers={"PRIVATE-TOKEN": token},
+    )
+    try:
+        res = urllib.request.urlopen(req_project_metadata)
+        json_res = json.load(res)
+        path_with_namespace = json_res["path_with_namespace"]
+    except urllib.error.HTTPError as e:
+        print("ERROR:", e.read().decode())
+        raise
+
+    return path_with_namespace
+
+
 def push_merge_request(
     project_url, proposed_branch, upstream, downstream, auto_merge="", dry_run=False
 ):
@@ -215,8 +244,11 @@ def push_merge_request(
 
     ensure_downstream_branch(project_url, downstream, dry_run)
 
+    project_path = get_path_with_namespace(project_url)
+
     print(
-        f"Create merge request from '{proposed_branch}' to '{downstream}'", flush=True
+        f"Create merge request from '{proposed_branch}' to '{project_path}/{downstream}'",
+        flush=True,
     )
     git_push_custom(
         "-o",
@@ -226,6 +258,8 @@ def push_merge_request(
         "-o",
         f"merge_request.target={downstream}",
         "-o",
+        f"merge_request.target_project={project_path}",
+        "-o",
         f"merge_request.title={title}",
         project_url,
         f"{proposed_branch}:{proposed_branch}",
@@ -327,6 +361,7 @@ def main():
                     "--is-ancestor",
                     upstream_branch,
                     downstream_branch,
+                    **RETURN_CMD,
                     _ok_code=[0, 1],
                 ).exit_code
                 == 0
@@ -406,7 +441,11 @@ def main():
                 conflicts.append(conflict)
             else:
                 o = git(
-                    "diff", "--quiet", f"HEAD..{downstream_branch}", _ok_code=[0, 1]
+                    "diff",
+                    "--quiet",
+                    f"HEAD..{downstream_branch}",
+                    **RETURN_CMD,
+                    _ok_code=[0, 1],
                 )
                 if o.exit_code == 0:
                     print(f"No merge required for {downstream_branch}, skipping ")
diff --git a/tools/apertis-pkg-pull-updates b/tools/apertis-pkg-pull-updates
index 3dbc3417d5522da2aa7eb0f3322997eecc3d075a..7f71c77806efae6eae7140dcbe2217c3363e3258 100755
--- a/tools/apertis-pkg-pull-updates
+++ b/tools/apertis-pkg-pull-updates
@@ -23,8 +23,11 @@ from itertools import chain
 import yaml
 from debian.changelog import Changelog
 from debian.debian_support import Version
+from sh import __version__ as sh_version
 from sh.contrib import git
 
+RETURN_CMD = {} if sh_version.startswith("1.") else {"_return_cmd": True}
+
 
 def debian_branch(suite):
     return "debian/" + suite
@@ -55,7 +58,15 @@ def parse_ref(ref: str) -> str:
 
 def is_ancestor(this: str, other: str):
     return (
-        git("merge-base", "--is-ancestor", this, other, _ok_code=[0, 1]).exit_code == 0
+        git(
+            "merge-base",
+            "--is-ancestor",
+            this,
+            other,
+            **RETURN_CMD,
+            _ok_code=[0, 1],
+        ).exit_code
+        == 0
     )
 
 
@@ -433,6 +444,7 @@ def main():
                     "merge",
                     "--ff-only",
                     local_version_branch,
+                    **RETURN_CMD,
                     _out="/dev/stdout",
                     _err="/dev/stderr",
                 )
diff --git a/tools/import-debian-package b/tools/import-debian-package
index bad0725787bb109b4397fca89c93b80f978bd4ec..eda6a4762a0215a905ecc15858bc67f7b33907f2 100755
--- a/tools/import-debian-package
+++ b/tools/import-debian-package
@@ -19,7 +19,7 @@ import sys
 from functools import lru_cache
 from gettext import gettext as _
 from pathlib import Path
-from tempfile import TemporaryDirectory
+from tempfile import TemporaryDirectory, mkdtemp
 from typing import Mapping, Optional
 from urllib.error import HTTPError, URLError
 from urllib.parse import quote
@@ -393,32 +393,36 @@ def do_import(args):
     if parse_ref(apertis_tag):
         raise Abort(f"Apertis tag {apertis_tag} already exists, cannot continue")
 
-    with ensure_dir_or_none(args.cache_dir) or TemporaryDirectory() as downloaddir:
-        if args.import_local_dsc:
-            logging.info(f"Overlaying Apertis package version {version}")
-            downloaded = args.import_local_dsc.name
-        else:
-            logging.info("Fetching the package information")
+    downloaddir = (
+        ensure_dir_or_none(args.cache_dir)
+        if ensure_dir_or_none(args.cache_dir) is not None
+        else mkdtemp()
+    )
+    if args.import_local_dsc:
+        logging.info(f"Overlaying Apertis package version {version}")
+        downloaded = args.import_local_dsc.name
+    else:
+        logging.info("Fetching the package information")
 
-            cached_dsc = get_cached_dsc(package, version, args.cache_dir)
+        cached_dsc = get_cached_dsc(package, version, args.cache_dir)
 
-            try:
-                url = args.mirror.rstrip("/") + get_debian_dsc_path(package, version)
-            except KeyboardInterrupt:
-                raise
-            except (KeyError, HTTPError, URLError):
-                if not cached_dsc:
-                    raise Abort(
-                        f"Unable to download source package {package} version {version}"
-                    )
-                else:
-                    logging.warning(
-                        f"Unable to download source package {package} version {version}, but found cached .dsc"
-                    )
-                    url = cached_dsc
-
-            downloaded = fetch(package, version, url, downloaddir, cached_dsc)
-        import_debian_dsc(args, downloaded, upstream, downstream, version)
+        try:
+            url = args.mirror.rstrip("/") + get_debian_dsc_path(package, version)
+        except KeyboardInterrupt:
+            raise
+        except (KeyError, HTTPError, URLError):
+            if not cached_dsc:
+                raise Abort(
+                    f"Unable to download source package {package} version {version}"
+                )
+            else:
+                logging.warning(
+                    f"Unable to download source package {package} version {version}, but found cached .dsc"
+                )
+                url = cached_dsc
+
+        downloaded = fetch(package, version, url, downloaddir, cached_dsc)
+    import_debian_dsc(args, downloaded, upstream, downstream, version)
 
     if args.dsc:
         d = deb822.Dsc(args.dsc)