# HG changeset patch # User Raphaël Gomès # Date 1730191128 -3600 # Node ID de4b9ea2fa348ce1de120869d1e43fd37a1f0e42 # Parent d5759303041612e66e5655de92749fe05866b15a# Parent 0de1895c2218635246da22a42a2e70633cdee738 branching: merge stable into default diff -r d57593030416 -r de4b9ea2fa34 contrib/heptapod-ci.yml --- 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 diff -r d57593030416 -r de4b9ea2fa34 tests/run-tests.py --- 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)