branching: merge stable into default
authorRaphaël Gomès <rgomes@octobus.net>
Tue, 29 Oct 2024 09:38:48 +0100
changeset 52152 de4b9ea2fa34
parent 52146 d57593030416 (current diff)
parent 52151 0de1895c2218 (diff)
child 52158 0744248cc541
branching: merge stable into default
--- a/contrib/heptapod-ci.yml	Mon Oct 28 16:31:49 2024 +0100
+++ b/contrib/heptapod-ci.yml	Tue Oct 29 09:38:48 2024 +0100
@@ -20,7 +20,12 @@
       when: always
 
 stages:
+  - build
+  - checks
   - tests
+  - platform-compat
+  - py-version-compat
+
 
 image: registry.heptapod.net/mercurial/ci-images/mercurial-core:$HG_CI_IMAGE_TAG
 
@@ -32,7 +37,37 @@
 
 .all_template: &all
   when: on_success
+  needs: []
 
+# TODO: we should use an image based on manylinux instead "all-in-one" image
+# used for all test so far.
+.build-wheel: &wheel
+    <<: *all
+    stage: build
+    variables:
+      WHEEL_TYPE: ""
+      FLAVOR: ""
+    before_script:
+      - echo "python used, $PYTHON"
+      - $PYTHON --version
+      - echo $WHEEL_TYPE
+      - test -n "$WHEEL_TYPE"
+      - echo $FLAVOR
+      - mkdir -p wheels/$WHEEL_TYPE
+    script:
+      - $PYTHON setup.py bdist_wheel $FLAVOR --dist-dir wheels/$WHEEL_TYPE
+    artifacts:
+      paths:
+        - wheels/$WHEEL_TYPE
+      expire_in: 1 week
+
+build-c-wheel:
+    <<: *wheel
+    variables:
+      WHEEL_TYPE: "c"
+
+# TODO: We should select the wheel compatible with the python (and plateform)
+# we use. This is necessary to build multiple wheel.
 .runtests_template: &runtests
     <<: *all
     stage: tests
@@ -48,10 +83,23 @@
       - ls -1 tests/test-check-*.* > /tmp/check-tests.txt
     script:
         - echo "$RUNTEST_ARGS"
-        - HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" "$PYTHON" tests/run-tests.py --color=always $RUNTEST_ARGS
+        - echo "$WHEEL_TYPE"
+        - WHEEL=""
+        - if test -n "$WHEEL_TYPE"; then
+             WHEEL="`ls -1 $CI_PROJECT_DIR/wheels/$WHEEL_TYPE/*.whl`";
+             test -n "$WHEEL";
+          fi
+        - if test -n "$WHEEL"; then
+            echo installing from $WHEEL;
+            HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" "$PYTHON" tests/run-tests.py --hg-wheel $WHEEL --color=always $RUNTEST_ARGS;
+          else
+            echo installing from source;
+            HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" "$PYTHON" tests/run-tests.py --color=always $RUNTEST_ARGS;
+          fi
 
 checks:
     <<: *runtests
+    stage: checks
     variables:
         SHOW_VERSION_OF: "$PYTHON black clang-format"
         RUNTEST_ARGS: "--time --test-list /tmp/check-tests.txt"
@@ -59,19 +107,27 @@
 
 rust-cargo-test:
     <<: *all
-    stage: tests
+    stage: checks
     script:
         - make rust-tests
         - make cargo-clippy
     variables:
         CI_CLEVER_CLOUD_FLAVOR: S
 
-test-c: &test_c
+.test-c: &test_c
     <<: *runtests
     variables:
         RUNTEST_ARGS: " --no-rust --blacklist /tmp/check-tests.txt"
         TEST_HGTESTS_ALLOW_NETIO: "1"
 
+test-c:
+    <<: *test_c
+    needs: [build-c-wheel]
+    variables:
+        WHEEL_TYPE: "c"
+        RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt"
+        TEST_HGTESTS_ALLOW_NETIO: "1"
+
 test-pure:
     <<: *runtests
     variables:
@@ -99,36 +155,42 @@
 # version (and 3.12 have been giving us various troubles)
 test-3.8-c:
     <<: *test_c
+    stage: py-version-compat
     when: manual  # avoid overloading the CI by default
     variables:
         PYTHON: python3.8
 
 test-3.12-c:
     <<: *test_c
+    stage: py-version-compat
     when: manual  # avoid overloading the CI by default
     variables:
         PYTHON: python3.12
 
 test-3.12-rust:
     <<: *test_rust
+    stage: py-version-compat
     when: manual  # avoid overloading the CI by default
     variables:
         PYTHON: python3.12
 
 test-3.13-c:
     <<: *test_c
+    stage: py-version-compat
     when: manual  # avoid overloading the CI by default
     variables:
         PYTHON: python3.13
 
 test-3.13-rust:
     <<: *test_rust
+    stage: py-version-compat
     when: manual  # avoid overloading the CI by default
     variables:
         PYTHON: python3.13
 
 check-pytype:
-    extends: .runtests_template
+    <<: *test_rust
+    stage: checks
     before_script:
       - export PATH="/home/ci-runner/vendor/pyenv/pyenv-2.4.7-adf3c2bccf09cdb81febcfd15b186711a33ac7a8/shims:/home/ci-runner/vendor/pyenv/pyenv-2.4.7-adf3c2bccf09cdb81febcfd15b186711a33ac7a8/bin:$PATH"
       - echo "PATH, $PATH"
@@ -149,7 +211,7 @@
 .window_runtests_template: &windows_runtests
     <<: *all
     when: manual  # we don't have any Windows runners anymore at the moment
-    stage: tests
+    stage: platform-compat
     before_script:
       - C:/MinGW/msys/1.0/bin/sh.exe --login -c 'cd "$OLDPWD" && ls -1 tests/test-check-*.* > C:/Temp/check-tests.txt'
       # TODO: find/install cvs, bzr, perforce, gpg, sqlite3
@@ -183,6 +245,7 @@
 
 macos:
     <<: *test_c
+    stage: platform-compat
     when: manual  # avoid overloading the CI by default
     tags:
       - macos
--- a/tests/run-tests.py	Mon Oct 28 16:31:49 2024 +0100
+++ b/tests/run-tests.py	Tue Oct 29 09:38:48 2024 +0100
@@ -61,6 +61,7 @@
 import shlex
 import shutil
 import signal
+import site
 import socket
 import subprocess
 import sys
@@ -625,6 +626,13 @@
         help="prefer IPv6 to IPv4 for network related tests",
     )
     hgconf.add_argument(
+        "--hg-wheel",
+        default=None,
+        metavar="WHEEL_PATH",
+        dest="wheel",
+        help="install mercurial from the given wheel",
+    )
+    hgconf.add_argument(
         "--pure",
         action="store_true",
         help="use pure Python code instead of C extensions",
@@ -3219,7 +3227,12 @@
 
         # detect and enforce an alternative way to specify rust extension usage
         if (
-            not (self.options.pure or self.options.rust or self.options.no_rust)
+            not (
+                self.options.wheel
+                or self.options.pure
+                or self.options.rust
+                or self.options.no_rust
+            )
             and os.environ.get("HGWITHRUSTEXT") == "cpython"
         ):
             self.options.rust = True
@@ -3257,7 +3270,12 @@
             self._installdir = os.path.join(self._hgtmp, b"install")
             self._bindir = os.path.join(self._installdir, b"bin")
             self._hgcommand = b'hg'
-            self._pythondir = os.path.join(self._installdir, b"lib", b"python")
+
+            if self.options.wheel:
+                suffix = _sys2bytes(site.USER_SITE[len(site.USER_BASE) + 1 :])
+            else:
+                suffix = os.path.join(b"lib", b"python")
+            self._pythondir = os.path.join(self._installdir, suffix)
 
         # Force the use of hg.exe instead of relying on MSYS to recognize hg is
         # a python script and feed it to python.exe.  Legacy stdio is force
@@ -3782,59 +3800,113 @@
                 os.symlink(real_exec, target_exec)
             self._createdfiles.append(target_exec)
 
+    def _install_hg_cmd_wheel(self):
+        wheel_path = self.options.wheel
+        assert wheel_path
+
+        # TODO: actually use these flag later, to double check the wheel we
+        # installed match our intend (in `_checkhglib`)
+        if self.options.pure:
+            assert False, b"--pure"
+        elif self.options.rust:
+            assert False, b"--rust"
+        elif self.options.no_rust:
+            assert False, b"--no-rust"
+
+        script = _sys2bytes(os.path.realpath(sys.argv[0]))
+        exe = _sys2bytes(sysexecutable)
+        hgroot = os.path.dirname(os.path.dirname(script))
+        self._hgroot = hgroot
+        os.chdir(hgroot)
+        cmd = [
+            exe,
+            b"-m",
+            b"pip",
+            b"install",
+            wheel_path,
+            b"--force",
+            b"--ignore-installed",
+            b"--user",
+            b"--break-system-packages",
+        ]
+        if not WINDOWS:
+            # The --home="" trick works only on OS where os.sep == '/'
+            # because of a distutils convert_path() fast-path. Avoid it at
+            # least on Windows for now, deal with .pydistutils.cfg bugs
+            # when they happen.
+            # cmd.append(b"--global-option=--home=")
+            pass
+
+        return cmd
+
+    def _install_hg_cmd_setup(self):
+        # Run installer in hg root
+        setup_opts = b""
+        if self.options.pure:
+            setup_opts = b"--pure"
+        elif self.options.rust:
+            setup_opts = b"--rust"
+        elif self.options.no_rust:
+            setup_opts = b"--no-rust"
+
+        script = _sys2bytes(os.path.realpath(sys.argv[0]))
+        exe = _sys2bytes(sysexecutable)
+        hgroot = os.path.dirname(os.path.dirname(script))
+        self._hgroot = hgroot
+        os.chdir(hgroot)
+        cmd = [
+            exe,
+            b"setup.py",
+        ]
+        if setup_opts:
+            cmd.append(setup_opts)
+        cmd.extend(
+            [
+                b"clean",
+                b"--all",
+                b"build",
+            ]
+        )
+        if self.options.compiler:
+            cmd.append("--compiler")
+            cmd.append(_sys2bytes(self.options.compiler))
+        cmd.extend(
+            [
+                b"--build-base=%s" % os.path.join(self._hgtmp, b"build"),
+                b"install",
+                b"--force",
+                b"--prefix=%s" % self._installdir,
+                b"--install-lib=%s" % self._pythondir,
+                b"--install-scripts=%s" % self._bindir,
+            ]
+        )
+        if not WINDOWS:
+            # The --home="" trick works only on OS where os.sep == '/'
+            # because of a distutils convert_path() fast-path. Avoid it at
+            # least on Windows for now, deal with .pydistutils.cfg bugs
+            # when they happen.
+            cmd.append(b"--home=")
+
+        return cmd
+
     def _installhg(self):
         """Install hg into the test environment.
 
         This will also configure hg with the appropriate testing settings.
         """
         vlog("# Performing temporary installation of HG")
+        install_env = original_env.copy()
+        if self.options.wheel is None:
+            cmd = self._install_hg_cmd_setup()
+        else:
+            cmd = self._install_hg_cmd_wheel()
+            install_env["PYTHONUSERBASE"] = _bytes2sys(self._installdir)
+
         installerrs = os.path.join(self._hgtmp, b"install.err")
-        compiler = ''
-        install_env = original_env.copy()
-        if self.options.compiler:
-            compiler = '--compiler ' + self.options.compiler
-        setup_opts = b""
         if self.options.pure:
-            setup_opts = b"--pure"
-            install_env.pop('HGWITHRUSTEXT', None)
-        elif self.options.rust:
-            setup_opts = b"--rust"
-        elif self.options.no_rust:
-            setup_opts = b"--no-rust"
             install_env.pop('HGWITHRUSTEXT', None)
-
-        # Run installer in hg root
-        compiler = _sys2bytes(compiler)
-        script = _sys2bytes(os.path.realpath(sys.argv[0]))
-        exe = _sys2bytes(sysexecutable)
-        hgroot = os.path.dirname(os.path.dirname(script))
-        self._hgroot = hgroot
-        os.chdir(hgroot)
-        nohome = b'--home=""'
-        if WINDOWS:
-            # The --home="" trick works only on OS where os.sep == '/'
-            # because of a distutils convert_path() fast-path. Avoid it at
-            # least on Windows for now, deal with .pydistutils.cfg bugs
-            # when they happen.
-            nohome = b''
-        cmd = (
-            b'"%(exe)s" setup.py %(setup_opts)s clean --all'
-            b' build %(compiler)s --build-base="%(base)s"'
-            b' install --force --prefix="%(prefix)s"'
-            b' --install-lib="%(libdir)s"'
-            b' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
-            % {
-                b'exe': exe,
-                b'setup_opts': setup_opts,
-                b'compiler': compiler,
-                b'base': os.path.join(self._hgtmp, b"build"),
-                b'prefix': self._installdir,
-                b'libdir': self._pythondir,
-                b'bindir': self._bindir,
-                b'nohome': nohome,
-                b'logfile': installerrs,
-            }
-        )
+        elif self.options.no_rust:
+            install_env.pop('HGWITHRUSTEXT', None)
 
         # setuptools requires install directories to exist.
         def makedirs(p):
@@ -3846,8 +3918,15 @@
         makedirs(self._pythondir)
         makedirs(self._bindir)
 
-        vlog("# Running", cmd.decode("utf-8"))
-        if subprocess.call(_bytes2sys(cmd), shell=True, env=install_env) == 0:
+        vlog("# Running", cmd)
+        with open(installerrs, "wb") as logfile:
+            r = subprocess.call(
+                cmd,
+                env=install_env,
+                stdout=logfile,
+                stderr=subprocess.STDOUT,
+            )
+        if r == 0:
             if not self.options.verbose:
                 try:
                     os.remove(installerrs)