Mercurial > hg
changeset 52255:65d516db7309
branching: merge stable into default
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Thu, 14 Nov 2024 16:45:23 +0100 |
parents | 85bff84f0ad5 (current diff) d237fdd93eb9 (diff) |
children | 51a350a22d0c |
files | rust/Cargo.lock rust/hg-core/Cargo.toml rust/hg-core/src/errors.rs rust/hg-core/src/lib.rs rust/hg-core/src/update.rs |
diffstat | 33 files changed, 981 insertions(+), 212 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgsigs Mon Oct 21 12:58:40 2024 +0200 +++ b/.hgsigs Thu Nov 14 16:45:23 2024 +0100 @@ -267,3 +267,4 @@ 11a9e2fc0caf0800625612e374139e4d825349a6 0 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
--- a/.hgtags Mon Oct 21 12:58:40 2024 +0200 +++ b/.hgtags Thu Nov 14 16:45:23 2024 +0100 @@ -283,3 +283,4 @@ 11a9e2fc0caf0800625612e374139e4d825349a6 6.8.1 eae3ec345e5e62e653de32a87a70f6fa7241afde 6.8.2 dc97e8670decc9925c2f570bdca636778184b199 6.9rc0 +31d45a1cbc479ac73fc8b355abe99e090eb6c747 6.9rc1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/build-one-linux-wheel.sh Thu Nov 14 16:45:23 2024 +0100 @@ -0,0 +1,28 @@ +#!/bin/bash +# build a single linux wheel within a prepared imaged based on manylinux images +# +# +# +set -eu + +# enforce that the translation are built +export MERCURIAL_SETUP_FORCE_TRANSLATIONS=1 + +if [ $# -lt 2 ]; then + echo "usage $0 PYTHONTAG DEST_DIR" >&2 + echo "" >&2 + echo 'PYTHONTAG should be of the form "cp310-cp310"' >&2 + exit 64 +fi +py_tag=$1 +destination_directory=$2 + + +tmp_wheel_dir=./tmp-wheelhouse + +if [ -e $tmp_wheel_dir ]; then + rm -rf $tmp_wheel_dir +fi +/opt/python/$py_tag/bin/python setup.py bdist_wheel --dist-dir $tmp_wheel_dir +# adjust it to make it universal +auditwheel repair $tmp_wheel_dir/*.whl -w $destination_directory
--- a/contrib/check-pytype.sh Mon Oct 21 12:58:40 2024 +0200 +++ b/contrib/check-pytype.sh Thu Nov 14 16:45:23 2024 +0100 @@ -68,6 +68,13 @@ # TODO: include hgext and hgext3rd +# use ts to produce some timing if available +if ! command -v ts; then + ts() { + cat + } +fi + pytype --keep-going --jobs auto \ doc/check-seclevel.py hgdemandimport hgext mercurial \ -x hgext/absorb.py \ @@ -116,7 +123,8 @@ -x mercurial/testing/storage.py \ -x mercurial/thirdparty \ -x mercurial/win32.py \ - -x mercurial/wireprotov1server.py + -x mercurial/wireprotov1server.py \ + | ts -i "(%.s)" | ts -s "%.s" if find .pytype/pyi -name '*.pyi' | xargs grep -ql '# Caught error'; then echo 'pytype crashed while generating the following type stubs:'
--- a/contrib/heptapod-ci.yml Mon Oct 21 12:58:40 2024 +0200 +++ b/contrib/heptapod-ci.yml Thu Nov 14 16:45:23 2024 +0100 @@ -25,6 +25,7 @@ - tests - platform-compat - py-version-compat + - upload image: registry.heptapod.net/mercurial/ci-images/mercurial-core:$HG_CI_IMAGE_TAG @@ -32,81 +33,144 @@ variables: PYTHON: python HG_CI_IMAGE_TAG: "v2.1" - TEST_HGTESTS_ALLOW_NETIO: "0" - SHOW_VERSION_OF: "$PYTHON" + # a directory dedicated to creating files and temporary clone + # with shell runner, its content is not cleaned from one call to the next, + # so plan for it. + TMP_WORK_DIR: "${CI_PROJECT_DIR}/../.." + # we use CIBW_SKIP="pp*" to prevent the building of pypy wheel that are neither + # needed nor working. + CIBW_SKIP: "pp*" -.all_template: &all +.all: + # help changing all job at once when debugging when: on_success + # make sure jobs from later steps does not wait for anything implicit before + # starting. 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 +# a dummy job that only serve to trigger others +# +# This is useful for two reasons: +# - the UX around parallel jobs is awful so manually starting them is unpractical +# - manual starting job cannot make the pipeline "fails" and block a merge, +# while "on_success" job depending on manual trigger works fine in that regard. +.trigger: + extends: .all + # smallest I know of + image: busybox + when: manual + variables: + GIT_STRATEGY: none + CI_CLEVER_CLOUD_FLAVOR: "XS" + script: + - echo 'nothing to see here' + +.build-wheel: + extends: .all + image: "registry.heptapod.net/mercurial/ci-images/core-wheel-x86_64-c:v3.0" + stage: build + variables: + WHEEL_TYPE: "" + FLAVOR: "" + MERCURIAL_SETUP_FORCE_TRANSLATIONS: "1" + CI_CLEVER_CLOUD_FLAVOR: "XS" + script: + - PLATFORM=`/opt/python/cp313-cp313/bin/python -c 'import sys; print(sys.platform)'` + - echo $WHEEL_TYPE + - test -n "$WHEEL_TYPE" + - echo $FLAVOR + - mkdir -p wheels/$PLATFORM/$WHEEL_TYPE/$BUILD_PY_ID + - contrib/build-one-linux-wheel.sh $BUILD_PY_ID wheels/$PLATFORM/$WHEEL_TYPE/$BUILD_PY_ID + artifacts: + paths: + - wheels/ + expire_in: 1 week build-c-wheel: - <<: *wheel - variables: - WHEEL_TYPE: "c" + extends: .build-wheel + variables: + WHEEL_TYPE: "c" + parallel: + matrix: + - BUILD_PY_ID: + - cp38-cp38 + - cp39-cp39 + - cp310-cp310 + - cp311-cp311 + - cp312-cp312 + - cp313-cp313 -# 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 +.runtests: + extends: .all stage: tests + variables: + SHOW_VERSION_OF: "$PYTHON" + TEST_HGTESTS_ALLOW_NETIO: "0" + FILTER: "" + FLAVOR: "" + RUNTEST_ARGS: "" # The runner made a clone as root. # We make a new clone owned by user used to run the step. before_script: - echo "python used, $PYTHON" - for tool in $SHOW_VERSION_OF ; do echo '#' version of $tool; $tool --version; done - - rm -rf /tmp/mercurial-ci/ # Clean slate if not using containers - - hg clone . /tmp/mercurial-ci/ --noupdate --config phases.publish=no - - hg -R /tmp/mercurial-ci/ update `hg log --rev '.' --template '{node}'` - - cd /tmp/mercurial-ci/ - - ls -1 tests/test-check-*.* > /tmp/check-tests.txt + - rm -rf "${TMP_WORK_DIR}"/mercurial-ci/ # Clean slate if not using containers + - hg clone . "${TMP_WORK_DIR}"/mercurial-ci/ --noupdate --config phases.publish=no + - hg -R "${TMP_WORK_DIR}"/mercurial-ci/ update `hg log --rev '.' --template '{node}'` + - cd "${TMP_WORK_DIR}"/mercurial-ci/ + - ls -1 tests/test-check-*.* > "${TMP_WORK_DIR}"/check-tests.txt script: + - echo "$TEST_HGTESTS_ALLOW_NETIO" - echo "$RUNTEST_ARGS" + - echo "$FILTER" + - echo "$FLAVOR" - echo "$WHEEL_TYPE" - - WHEEL="" + - PORT_START=`expr 19051 + 1009 '*' $CI_CONCURRENT_ID` + - PORT_ARG="--port $PORT_START" + - echo $PORT_ARG + - PLATFORM=`$PYTHON -c 'import sys; print(sys.platform)'` + - echo $PLATFORM + - WHEEL_ARG="" + - SHARDING_ARGS="" - if test -n "$WHEEL_TYPE"; then - WHEEL="`ls -1 $CI_PROJECT_DIR/wheels/$WHEEL_TYPE/*.whl`"; + PY_TAG=`$PYTHON -c 'import sys; v=sys.version_info; t=f"cp{v.major}{v.minor}"; print(f"{t}-{t}")'`; + echo "$PY_TAG"; + test -n "PY_TAG"; + WHEEL="`ls -1 $CI_PROJECT_DIR/wheels/$PLATFORM/$WHEEL_TYPE/$PY_TAG/*.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; + echo installing from $WHEEL; + WHEEL_ARG="--hg-wheel $WHEEL"; + echo disabling flavor as this is currently incompatible with '"--hg-wheel"'; + FLAVOR=""; else echo installing from source; - HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" "$PYTHON" tests/run-tests.py --color=always $RUNTEST_ARGS; + fi; + - if [ -n "$CI_NODE_INDEX" ]; then + echo "Running the test in multiple shard - [$CI_NODE_INDEX/$CI_NODE_TOTAL]"; + SHARDING_ARGS="--shard-index $CI_NODE_INDEX --shard-total $CI_NODE_TOTAL"; + echo "sharding... $SHARDING_ARGS"; fi + - HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" + "$PYTHON" tests/run-tests.py + --color=always + $PORT_ARG + $WHEEL_ARG + $FLAVOR + $SHARDING_ARGS + $FILTER + $RUNTEST_ARGS; checks: - <<: *runtests + extends: .runtests stage: checks variables: SHOW_VERSION_OF: "$PYTHON black clang-format" - RUNTEST_ARGS: "--time --test-list /tmp/check-tests.txt" + RUNTEST_ARGS: "--time" + FILTER: "--test-list ${TMP_WORK_DIR}/check-tests.txt" CI_CLEVER_CLOUD_FLAVOR: S rust-cargo-test: - <<: *all + extends: .all stage: checks script: - make rust-tests @@ -114,89 +178,120 @@ variables: CI_CLEVER_CLOUD_FLAVOR: S -.test-c: &test_c - <<: *runtests +.runtests-no-check: + extends: .runtests + variables: + FILTER: "--blacklist ${TMP_WORK_DIR}/check-tests.txt" + TEST_HGTESTS_ALLOW_NETIO: "1" + +.test-c: + extends: .runtests-no-check variables: - RUNTEST_ARGS: " --no-rust --blacklist /tmp/check-tests.txt" - TEST_HGTESTS_ALLOW_NETIO: "1" + FLAVOR: "--no-rust" test-c: - <<: *test_c - needs: [build-c-wheel] + extends: .test-c + needs: + - job: build-c-wheel + parallel: + matrix: + - BUILD_PY_ID: "cp311-cp311" variables: WHEEL_TYPE: "c" - RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt" - TEST_HGTESTS_ALLOW_NETIO: "1" test-pure: - <<: *runtests + extends: .runtests-no-check variables: - RUNTEST_ARGS: "--pure --blacklist /tmp/check-tests.txt" + FLAVOR: "--pure" -test-rust: &test_rust - <<: *runtests +test-rust: + extends: .runtests-no-check variables: - HGWITHRUSTEXT: cpython - RUNTEST_ARGS: "--rust --blacklist /tmp/check-tests.txt" + HGWITHRUSTEXT: "cpython" + FLAVOR: "--rust" test-rhg: - <<: *runtests + extends: .runtests-no-check variables: - HGWITHRUSTEXT: cpython - RUNTEST_ARGS: "--rust --rhg --blacklist /tmp/check-tests.txt" + HGWITHRUSTEXT: "cpython" + FLAVOR: "--rust --rhg" test-chg: - <<: *runtests + extends: .runtests-no-check variables: - RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt --chg" + FLAVOR: "--chg" + + +trigger-pycompat: + extends: .trigger + stage: py-version-compat + +.test-c-pycompat: + extends: .test-c + stage: py-version-compat + needs: + - trigger-pycompat + variables: + WHEEL_TYPE: "c" # note: we should probably get a full matrix for flavor × py-version, but this # is a simple start to be able to check if we break the lowest supported # 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 + extends: .test-c-pycompat variables: PYTHON: python3.8 + needs: + - job: build-c-wheel + parallel: + matrix: + - BUILD_PY_ID: "cp38-cp38" test-3.12-c: - <<: *test_c - stage: py-version-compat - when: manual # avoid overloading the CI by default + extends: .test-c-pycompat variables: PYTHON: python3.12 + needs: + - job: build-c-wheel + parallel: + matrix: + - BUILD_PY_ID: "cp312-cp312" test-3.12-rust: - <<: *test_rust + extends: test-rust stage: py-version-compat - when: manual # avoid overloading the CI by default + needs: + - trigger-pycompat variables: PYTHON: python3.12 test-3.13-c: - <<: *test_c - stage: py-version-compat - when: manual # avoid overloading the CI by default + extends: .test-c-pycompat variables: PYTHON: python3.13 + needs: + - job: build-c-wheel + parallel: + matrix: + - BUILD_PY_ID: "cp313-cp313" test-3.13-rust: - <<: *test_rust + extends: test-rust stage: py-version-compat - when: manual # avoid overloading the CI by default + needs: + - trigger-pycompat variables: PYTHON: python3.13 check-pytype: - <<: *test_rust + extends: 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" - - hg clone . /tmp/mercurial-ci/ --noupdate --config phases.publish=no - - hg -R /tmp/mercurial-ci/ update `hg log --rev '.' --template '{node}'` - - cd /tmp/mercurial-ci/ + - hg clone . "${TMP_WORK_DIR}"/mercurial-ci/ --noupdate --config phases.publish=no + - hg -R "${TMP_WORK_DIR}"/mercurial-ci/ update `hg log --rev '.' --template '{node}'` + - cd "${TMP_WORK_DIR}"/mercurial-ci/ - make local PYTHON=$PYTHON - ./contrib/setup-pytype.sh script: @@ -208,44 +303,217 @@ # is stored in OLDPWD. Of the added variables, MSYSTEM is crucial to running # run-tests.py- it is needed to make run-tests.py generate a `python3` script # that satisfies the various shebang lines and delegates to `py -3`. -.window_runtests_template: &windows_runtests - <<: *all + +.windows: + extends: .all when: manual # we don't have any Windows runners anymore at the moment + tags: + - windows + before_script: + - C:/hgdev/MinGW/msys/1.0/bin/sh.exe --login -c 'cd "$OLDPWD" && ls -1 tests/test-check-*.* > "${TMP_WORK_DIR}"/check-tests.txt' + # TODO: find/install cvs, bzr, perforce, gpg, sqlite3 + variables: + PYTHON: C:/hgdev/venvs/python39-x64/Scripts/python.exe + +# a dummy job that only serve to trigger the wider windows build +trigger-wheel-windows: + extends: .trigger + stage: build + +build-c-wheel-windows: + extends: .windows + stage: build + # wait for someone to click on "trigger-wheel-windows" + when: on_success + needs: + - "trigger-wheel-windows" + variables: + MERCURIAL_SETUP_FORCE_TRANSLATIONS: "1" + script: + - echo "Entering script section" + - echo "python used, $Env:PYTHON" + - Invoke-Expression "$Env:PYTHON -V" + - echo "$Env:RUNTEST_ARGS" + - echo "$Env:TMP" + - echo "$Env:TEMP" + - "C:/hgdev/venvs/python39-x64/Scripts/python.exe -m cibuildwheel --output-dir wheels/win32" + artifacts: + paths: + - wheels + expire_in: 1 week + parallel: + matrix: + # "cp39" is first as it unlock the tests + - CIBW_BUILD: + - "cp39-*" + - "cp38-*" + - "cp310-*" + - "cp311-*" + - "cp312-*" + - "cp313-*" + CIBW_ARCHS: + - "AMD64" + - "x86" + - CIBW_BUILD: + - "cp311-*" + - "cp312-*" + - "cp313-*" + CIBW_ARCHS: + - "ARM64" + + +.windows-runtests: + extends: .windows 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 - + # the UX for manual parallel jobs is quite awful, and the job que depends + # upon are manual anyway, so we can make this start automatically once the + # associated wheel is ready. + when: on_success + parallel: 20 script: - echo "Entering script section" - echo "python used, $Env:PYTHON" - Invoke-Expression "$Env:PYTHON -V" - - Invoke-Expression "$Env:PYTHON -m black --version" + - echo "$Env:HGTESTS_ALLOW_NETIO" + - echo "$Env:WHEEL_ARG" + - echo "$Env:FLAVOR" + - echo "$Env:FILTER" - echo "$Env:RUNTEST_ARGS" - echo "$Env:TMP" - echo "$Env:TEMP" + # This test is hanging the worker and not that important, so lets skip + # it for now + - C:/hgdev/MinGW/msys/1.0/bin/sh.exe -c 'cd "$OLDPWD" && echo tests/test-clonebundles-autogen.t > $TMP_WORK_DIR/windows-skip.txt' - - C:/MinGW/msys/1.0/bin/sh.exe --login -c 'cd "$OLDPWD" && HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" $PYTHON tests/run-tests.py --color=always $RUNTEST_ARGS' + - C:/hgdev/MinGW/msys/1.0/bin/sh.exe + --login -c 'cd "$OLDPWD" + && HGTESTS_ALLOW_NETIO="$TEST_HGTESTS_ALLOW_NETIO" + $PYTHON tests/run-tests.py + --color=always + $WHEEL_ARG + $FLAVOR + --port `expr 19051 + 1009 "*" $CI_CONCURRENT_ID` + --shard-index $CI_NODE_INDEX --shard-total $CI_NODE_TOTAL + $FILTER + $RUNTEST_ARGS; + ' + variables: + WHEEL_ARG: "" + RUNTEST_ARGS: "" + FLAVOR: "" + FILTER: "--blacklist ${TMP_WORK_DIR}/check-tests.txt --blacklist ${TMP_WORK_DIR}/windows-skip.txt" windows: - <<: *windows_runtests - tags: - - windows + extends: .windows-runtests variables: - RUNTEST_ARGS: "--blacklist C:/Temp/check-tests.txt" - PYTHON: py -3 + RUNTEST_ARGS: "" + WHEEL_ARG: "--hg-wheel wheels/win32/mercurial-*-cp39-cp39-win_amd64.whl" + needs: + - job: build-c-wheel-windows + parallel: + matrix: + - CIBW_BUILD: "cp39-*" + CIBW_ARCHS: "AMD64" windows-pyox: - <<: *windows_runtests - tags: - - windows + extends: .windows-runtests + when: manual # pyoxidizer builds seem broken with --no-use-pep517 variables: - RUNTEST_ARGS: "--blacklist C:/Temp/check-tests.txt --pyoxidized" - PYTHON: py -3 + FLAVOR: "--pyoxidized" macos: - <<: *test_c + extends: .test-c stage: platform-compat + # run the test in multiple shard to help spread the load between concurrent + # MR as the macos runner is a shell runner there is not startup overhead + # for tests. + parallel: 10 + tags: + - macos + variables: + WHEEL_TYPE: "c" + needs: + - build-c-wheel-macos + +# We could use CIBW_BUILD="cp310-*" to only build the Python 3.10 wheel for now as +# this is the only one we need to test. However testing that build work on all +# version is useful and match what we do with Linux. +# +# CIBW_SKIP is set globally at the start of the file. See comment there. +# +# The weird directory structure match the one we use for Linux to deal with the +# multiple jobs. (all this might be unnecessary) +build-c-wheel-macos: when: manual # avoid overloading the CI by default + stage: build tags: - macos + variables: + MERCURIAL_SETUP_FORCE_TRANSLATIONS: "1" + script: + - PLATFORM=`$PYTHON -c 'import sys; print(sys.platform)'` + - rm -rf tmp-wheels + - cibuildwheel --output-dir tmp-wheels/ + - for py_version in cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311 cp312-cp312 cp313-cp313; do + mkdir -p wheels/$PLATFORM/c/$py_version/; + mv tmp-wheels/*$py_version*.whl wheels/$PLATFORM/c/$py_version/; + done + - rm -rf tmp-wheels + artifacts: + paths: + - wheels + expire_in: 1 week + +# Upload nightly build wheel on the heptapod registry on test success +# +# At the time this task is added, since the mac wheels are built on shell +# runner, those nightly are not be considered fully secured. +# +# In addition, since any job can upload package, pretty much anyone with CI +# access can upload anything pretending to be any version. To fix it we would +# have to prevent the CI token to upload to the registry and have dedicated +# credential accessible only from protected branches. +upload-wheel-nightly: + extends: .all + image: "registry.heptapod.net/mercurial/ci-images/twine:v3.0" + stage: upload + # because we don't want to upload only half of a wheel + interruptible: false + rules: + - if: $CI_COMMIT_BRANCH =~ /^branch\/.*/ + # note that at the time of writing this, this job depends on multiple + # manual one. So it will not run by default, but will automatically run + # if the manual jobs are triggered. + # + # Also beware that "on_success" will ignore failure of manual test we + # directly depends on. This currently relevant for the "test-3.x-c" + # tests. + when: on_success + - if: $CI_COMMIT_BRANCH =~ /^topic\/.*/ + when: never + # if you need to test this, make it + # when: manual + # allow_failure: true + needs: + - build-c-wheel + - build-c-wheel-macos + - build-c-wheel-windows + - test-c + - macos + # if we also requires windows to be happy, reach the "50 needed jobs" limit. + # So we need some intermediate job to reduce the number. + # - windows + - test-3.8-c + - test-3.12-c + - test-3.13-c + # It would be nice to be able to restrict that a bit to protected branch only + variables: + TWINE_USERNAME: gitlab-ci-token + TWINE_PASSWORD: $CI_JOB_TOKEN + script: + - twine + upload + --verbose + --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi + wheels/*/*/*/*.whl + wheels/*/*.whl
--- a/contrib/import-checker.py Mon Oct 21 12:58:40 2024 +0200 +++ b/contrib/import-checker.py Thu Nov 14 16:45:23 2024 +0100 @@ -231,7 +231,7 @@ yield 'importlib.machinery' # python3 only yield 'importlib.util' # python3 only yield 'packaging.version' - for m in 'fcntl', 'grp', 'pwd', 'termios': # Unix only + for m in 'fcntl', 'grp', 'pwd', 'select', 'termios': # Unix only yield m for m in 'cPickle', 'datetime': # in Python (not C) on PyPy yield m
--- a/contrib/packaging/Makefile Mon Oct 21 12:58:40 2024 +0200 +++ b/contrib/packaging/Makefile Thu Nov 14 16:45:23 2024 +0100 @@ -129,7 +129,7 @@ .PHONY: linux-wheels-x86_64 linux-wheels-x86_64: - docker run -e "HGTEST_JOBS=$(shell nproc)" --rm -ti -v `pwd`/../..:/src quay.io/pypa/manylinux1_x86_64 /src/contrib/packaging/build-linux-wheels.sh + docker run --rm -ti -v `pwd`/../..:/src registry.heptapod.net/mercurial/ci-images/core-wheel-x86_64-c:v3.0 /src/contrib/packaging/build-linux-wheels.sh .PHONY: linux-wheels-i686 linux-wheels-i686:
--- a/contrib/packaging/build-linux-wheels.sh Mon Oct 21 12:58:40 2024 +0200 +++ b/contrib/packaging/build-linux-wheels.sh Thu Nov 14 16:45:23 2024 +0100 @@ -1,34 +1,44 @@ #!/bin/bash -# This file is directly inspired by -# https://github.com/pypa/python-manylinux-demo/blob/master/travis/build-wheels.sh + +# Intended to run within docker using image: +# +# registry.heptapod.net/mercurial/ci-images/core-wheel-x86_64-c:v3.0 +# +# we might want to factor most of this with the associated mercurial-core CI +# definition. (i.e. move this script into a place where the CI can directly call it for its purpose) + set -e -x -PYTHON_TARGETS=$(ls -d /opt/python/cp27*/bin) +PYTHON_TARGETS="cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311 cp312-cp312 cp313-cp313" + +export MERCURIAL_SETUP_FORCE_TRANSLATIONS=1 -# Create an user for the tests -useradd hgbuilder +# We need to copy the repository to ensure: +# (1) we don't wrongly write roots files in the repository (or any other wrong +# users) +# (2) we don't reuse pre-compiled extension built outside for manylinux and +# therefor not compatible. +cp -r /src/ /tmp/src/ +cd /tmp/src/ +hg purge --all --no-confirm -# Bypass uid/gid problems -cp -R /src /io && chown -R hgbuilder:hgbuilder /io +export HGRCPATH=/tmp/build-config.rc +cat << EOF > $HGRCPATH +[trusted] +users=* +groups=* +EOF -# Compile wheels for Python 2.X -for PYBIN in $PYTHON_TARGETS; do - "${PYBIN}/pip" wheel /io/ -w wheelhouse/ +for py in $PYTHON_TARGETS; do + echo 'build wheel for' $py + # cleanup any previous wheel + tmp_wd="/tmp/wheels/$py/repaired" + rm -rf $tmp_wd + mkdir -p $tmp_wd + # build a new wheel + contrib/build-one-linux-wheel.sh $py $tmp_wd + # fix the owner back to the repository owner + chown `stat /src/ -c %u:%g` $tmp_wd/*.whl + mv $tmp_wd/*.whl /src/dist done -# Bundle external shared libraries into the wheels with -# auditwheel (https://github.com/pypa/auditwheel) repair. -# It also fix the ABI tag on the wheel making it pip installable. -for whl in wheelhouse/*.whl; do - auditwheel repair "$whl" -w /src/wheelhouse/ -done - -# Install packages and run the tests for all Python versions -cd /io/tests/ - -for PYBIN in $PYTHON_TARGETS; do - # Install mercurial wheel as root - "${PYBIN}/pip" install mercurial --no-index -f /src/wheelhouse - # But run tests as hgbuilder user (non-root) - su hgbuilder -c "\"${PYBIN}/python\" /io/tests/run-tests.py --with-hg=\"${PYBIN}/hg\" --blacklist=/io/contrib/packaging/linux-wheel-centos5-blacklist" -done
--- a/mercurial/hgweb/server.py Mon Oct 21 12:58:40 2024 +0200 +++ b/mercurial/hgweb/server.py Thu Nov 14 16:45:23 2024 +0100 @@ -8,7 +8,6 @@ from __future__ import annotations -import errno import os import socket import sys @@ -124,8 +123,7 @@ # I/O below could raise another exception. So log the original # exception first to ensure it is recorded. if not ( - isinstance(e, (OSError, socket.error)) - and e.errno == errno.ECONNRESET + isinstance(e, (ConnectionResetError, ConnectionAbortedError)) ): tb = "".join(traceback.format_exception(*sys.exc_info())) # We need a native-string newline to poke in the log
--- a/mercurial/interfaces/repository.py Mon Oct 21 12:58:40 2024 +0200 +++ b/mercurial/interfaces/repository.py Thu Nov 14 16:45:23 2024 +0100 @@ -95,6 +95,7 @@ # (this is a mutable set to let extension update it) CACHES_POST_CLONE = CACHES_ALL.copy() CACHES_POST_CLONE.discard(CACHE_FILE_NODE_TAGS) +CACHES_POST_CLONE.discard(CACHE_REV_BRANCH) class ipeerconnection(interfaceutil.Interface):
--- a/mercurial/merge.py Mon Oct 21 12:58:40 2024 +0200 +++ b/mercurial/merge.py Thu Nov 14 16:45:23 2024 +0100 @@ -2054,9 +2054,14 @@ repo.hook(b'preupdate', throw=True, parent1=xp1, parent2=xp2) # note that we're in the middle of an update repo.vfs.write(b'updatestate', p2.hex()) + num_cpus = ( + repo.ui.configint(b"worker", b"numcpus", None) + if repo.ui.configbool(b"worker", b"enabled") + else 1 + ) try: updated_count = rust_update_mod.update_from_null( - repo.root, p2.rev() + repo.root, p2.rev(), num_cpus ) except rust_update_mod.FallbackError: update_from_null_fallback = True
--- a/mercurial/streamclone.py Mon Oct 21 12:58:40 2024 +0200 +++ b/mercurial/streamclone.py Thu Nov 14 16:45:23 2024 +0100 @@ -582,8 +582,10 @@ """ # arbitrarily picked as "it seemed fine" and much higher than the current - # usage. - MAX_OPEN = 100 + # usage. The Windows value of 2 is actually 1 file open at a time, due to + # the `flush_count = self.MAX_OPEN // 2` and `self.MAX_OPEN - 1` threshold + # for flushing to disk in __call__(). + MAX_OPEN = 2 if pycompat.iswindows else 100 def __init__(self): self._counter = 0
--- a/relnotes/6.9 Mon Oct 21 12:58:40 2024 +0200 +++ b/relnotes/6.9 Thu Nov 14 16:45:23 2024 +0100 @@ -1,3 +1,86 @@ += Mercurial 6.9rc1 = + +/!\ These are release notes for a release candidate version. Any and all points can be reverted before the final release. + + * streamclone: disable the volatile file open handle optimization on Windows + * rust-update: make `update_from_null` respect `worker.numcpu` config option + * rust-update: handle SIGINT from long-running update threads + * rust-cpython: add a TODO about repo reuse + * pytype: add relative timestamp to the output if `ts` is available + * hgweb: skip logging ConnectionAbortedError + +Below are many, many changes that have to do with building/testing wheels, +adding some sharding to the CI and MacOS + Windows compatibility work: + + * run-tests: don't use shell call for subprocess + * run-tests: add a --hg-wheel options to test a pre-built wheel + * ci: unify the way `check-pytype` inherit the common setting + * ci: split the jobs on more stage + * ci: build a wheel and use it to run c tests + * tests: stabilize `test-extdiff.t` on macOS + * tests: disable `test-git-interop.t` with a requirements directive + * tests: disable a section of `test-hgrc.t` that may hit a zeroconf bug + * ci: add a runner for Windows 10 + * tests: treat `select` as a built-in module on Windows + * tests: disable a section of `test-paths.t` that may hit a zeroconf bug + * tests: conditionalize missing output in test-racy-mutations.t on Windows + * tests: add a "missing" tests for manifest content in test-racy-mutations.t + * tests: bump the wait timeouts in test-racy-mutations.t + * test-install: use the global hg for the install step + * test-install: glob instance of "python" in warning + * ci: pre-adjust some identation + * setup: add a way to force the setup to translate (or fail) + * ci: use smaller VM to build wheel + * ci: use a pre-setup many-linux image to build wheel + * ci: build (and use) wheel for all supported version + * ci: automatically compute the python tag we use to identify tag + * run-tests: install wheel using --prefix instead of --user + * pycompat: drop some now useless workaround for makedirs + * wheel: build mac os wheel through the CI + * ci: use the macos wheel to run tests + * ci: use extends instead of <<: *x + * ci: move some variables closer to their usage + * ci: rationalize variable usage + * ci: abstract the of absolute /tmp/ path + * ci: move the "tempory work dir" to "concurrency-safe" location + * ci: adjust the starting port range to runner concurrency + * ci: have the mac test run if you trigger building the mac wheel + * run-tests: implement crude sharding support + * ci: shard the test run on mac os X + * dev-version: change the scheme of non tagged version + * wheel: enforce that translation being build for macos wheel + * run-tests: focus on listing the selected test for the shard tests + * run-tests: cleanup the "output" directory after the related tests + * tests: drop PYTHONPATH manipulation in test-pushvars.t + * windows: work around argument size limitation in test-bookmarks-pushpull.t + * windows: adjust PYTHONPATH update in test-status-color.t + * ci: use a concurrency safe TMP dir on Windows + * ci: again common element into a `.windows` template + * ci: split the windows runtest invocation into more granular variables + * windows: skip test-clonebundles-autogen.t in the CI + * ci: adjust port range on windows too + * windows: simply rely on the PATH adjustment to find python.exe in tests + * wheel: assign CIBW_SKIP globally + * wheel: make --hg-wheel works on Windows + * wheel: build Windows wheels too + * wheel: explicitly list built architecture + * wheel: test the built wheel in the windows tests + * ci: shard the tests on windows too + * wheel: enforce that translation being build for windows wheel + * setup: remote a debug statement that slipped through + * setup: factor version computation in a function + * setup: use the same code to compute tag from archive + * wheel: add a platform level to the wheel directories + * wheel: add a job uploading nightly build + * wheels: factor the core of Linux wheel building into a script + * wheels: update the Linux wheels make target + * clone: properly exclude rev-branch-cache from post clone cache warming + * setup: make sure Rust build its extension for the right python + * setup: preserve version part after the "+" on Windows + * wheel: build windows wheel for ARM64 too + * ci: adds a trigger for all pycompat jobs + * ci: let the Windows runner decide how many job they want to run + = Mercurial 6.9rc0 = /!\ These are release notes for a release candidate version. Any and all points can be reverted before the final release.
--- a/rust/Cargo.lock Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/Cargo.lock Thu Nov 14 16:45:23 2024 +0100 @@ -171,6 +171,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] name = "chrono" version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -350,6 +356,16 @@ ] [[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + +[[package]] name = "cxx" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -621,6 +637,7 @@ "chrono", "clap", "crossbeam-channel", + "ctrlc", "derive_more", "dyn-clone", "filetime", @@ -898,6 +915,18 @@ ] [[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] name = "nom8" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1723,6 +1752,15 @@ ] [[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index"
--- a/rust/hg-core/Cargo.toml Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-core/Cargo.toml Thu Nov 14 16:45:23 2024 +0100 @@ -12,6 +12,7 @@ bitflags = "1.3.2" bytes-cast = "0.3.0" byteorder = "1.4.3" +ctrlc = "3.4" derive_more = "0.99.17" hashbrown = { version = "0.13.1", features = ["rayon"] } home = "0.5.4"
--- a/rust/hg-core/src/errors.rs Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-core/src/errors.rs Thu Nov 14 16:45:23 2024 +0100 @@ -52,6 +52,8 @@ RaceDetected(String), /// An invalid path was found Path(HgPathError), + /// An interrupt was received and we need to stop whatever we're doing + InterruptReceived, } /// Details about where an I/O error happened @@ -125,6 +127,7 @@ write!(f, "encountered a race condition {context}") } HgError::Path(hg_path_error) => write!(f, "{}", hg_path_error), + HgError::InterruptReceived => write!(f, "interrupt received"), } } }
--- a/rust/hg-core/src/lib.rs Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-core/src/lib.rs Thu Nov 14 16:45:23 2024 +0100 @@ -53,10 +53,14 @@ parse_pattern_syntax_kind, read_pattern_file, IgnorePattern, PatternFileWarning, PatternSyntax, }; -use std::collections::HashMap; use std::fmt; +use std::{collections::HashMap, sync::atomic::AtomicBool}; use twox_hash::RandomXxHashBuilder64; +/// Used to communicate with threads spawned from code within this crate that +/// they should stop their work (SIGINT was received). +pub static INTERRUPT_RECEIVED: AtomicBool = AtomicBool::new(false); + pub type LineNumber = usize; /// Rust's default hasher is too slow because it tries to prevent collision
--- a/rust/hg-core/src/update.rs Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-core/src/update.rs Thu Nov 14 16:45:23 2024 +0100 @@ -5,6 +5,7 @@ io::Write, os::unix::fs::{MetadataExt, PermissionsExt}, path::Path, + sync::atomic::Ordering, time::Duration, }; @@ -24,12 +25,13 @@ revlog::RevlogError, sparse, utils::{ + cap_default_rayon_threads, files::{filesystem_now, get_path_from_bytes}, hg_path::{hg_path_to_path_buf, HgPath, HgPathError}, path_auditor::PathAuditor, }, vfs::{is_on_nfs_mount, VfsImpl}, - DirstateParents, UncheckedRevision, + DirstateParents, UncheckedRevision, INTERRUPT_RECEIVED, }; use crossbeam_channel::{Receiver, Sender}; use rayon::prelude::*; @@ -50,6 +52,7 @@ repo: &Repo, to: UncheckedRevision, progress: &dyn Progress, + workers: Option<usize>, ) -> Result<usize, HgError> { // Ignore the warnings, they've been displayed by Python already // TODO non-Python clients: display narrow warnings @@ -103,6 +106,15 @@ let chunks = chunk_tracked_files(tracked_files); progress.update(0, Some(files_count as u64)); + // TODO find a way (with `nix` or `signal-hook`?) of resetting the + // previous signal handler directly after. Currently, this is Python's + // job, but: + // - it introduces a (small) race between catching and resetting + // - it would break signal handlers in other contexts like `rhg`` + let _ = ctrlc::set_handler(|| { + INTERRUPT_RECEIVED.store(true, Ordering::Relaxed) + }); + create_working_copy( chunks, working_directory_path, @@ -111,8 +123,15 @@ files_sender, errors_sender, progress, + workers, ); + // Reset the global interrupt now that we're done + if INTERRUPT_RECEIVED.swap(false, Ordering::Relaxed) { + // The threads have all exited early, let's re-raise + return Err(HgError::InterruptReceived); + } + let errors: Vec<HgError> = errors_receiver.iter().collect(); if !errors.is_empty() { log::debug!("{} errors during update (see trace logs)", errors.len()); @@ -182,6 +201,7 @@ } #[logging_timer::time("trace")] +#[allow(clippy::too_many_arguments)] fn create_working_copy<'a: 'b, 'b>( chunks: Vec<(&HgPath, Vec<ExpandedManifestEntry<'a>>)>, working_directory_path: &Path, @@ -190,9 +210,11 @@ files_sender: Sender<(&'b HgPath, u32, usize, TruncatedTimestamp)>, error_sender: Sender<HgError>, progress: &dyn Progress, + workers: Option<usize>, ) { let auditor = PathAuditor::new(working_directory_path); - chunks.into_par_iter().for_each(|(dir_path, chunk)| { + + let work_closure = |(dir_path, chunk)| -> Result<(), HgError> { if let Err(e) = working_copy_worker( dir_path, chunk, @@ -207,7 +229,37 @@ .send(e) .expect("channel should not be disconnected") } - }); + Ok(()) + }; + if let Some(workers) = workers { + if workers > 1 { + // Work in parallel, potentially restricting the number of threads + match rayon::ThreadPoolBuilder::new().num_threads(workers).build() + { + Err(error) => error_sender + .send(HgError::abort( + error.to_string(), + exit_codes::ABORT, + None, + )) + .expect("channel should not be disconnected"), + Ok(pool) => { + log::trace!("restricting update to {} threads", workers); + pool.install(|| { + let _ = + chunks.into_par_iter().try_for_each(work_closure); + }); + } + } + } else { + // Work sequentially, don't even invoke rayon + let _ = chunks.into_iter().try_for_each(work_closure); + } + } else { + // Work in parallel by default in the global threadpool + let _ = cap_default_rayon_threads(); + let _ = chunks.into_par_iter().try_for_each(work_closure); + } } /// Represents a work unit for a single thread, responsible for this set of @@ -228,6 +280,11 @@ let dir_path = working_directory_path.join(dir_path); std::fs::create_dir_all(&dir_path).when_writing_file(&dir_path)?; + if INTERRUPT_RECEIVED.load(Ordering::Relaxed) { + // Stop working, the user has requested that we stop + return Err(HgError::InterruptReceived); + } + for (file, file_node, flags) in chunk { auditor.audit_path(file)?; let flags = flags.map(|f| f.into());
--- a/rust/hg-core/src/utils.rs Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-core/src/utils.rs Thu Nov 14 16:45:23 2024 +0100 @@ -542,7 +542,7 @@ /// Force the global rayon threadpool to not exceed 16 concurrent threads /// unless the user has specified a value. /// This is a stop-gap measure until we figure out why using more than 16 -/// threads makes `status` slower for each additional thread. +/// threads makes `status` and `update` slower for each additional thread. /// /// TODO find the underlying cause and fix it, then remove this. ///
--- a/rust/hg-cpython/src/update.rs Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-cpython/src/update.rs Thu Nov 14 16:45:23 2024 +0100 @@ -15,18 +15,24 @@ use crate::{ exceptions::FallbackError, - utils::{hgerror_to_pyerr, repo_from_path}, + utils::{hgerror_to_pyerr, repo_from_path, with_sigint_wrapper}, }; pub fn update_from_null_fast_path( py: Python, repo_path: PyObject, to: BaseRevision, + num_cpus: Option<usize>, ) -> PyResult<usize> { log::trace!("Using update from null fastpath"); let repo = repo_from_path(py, repo_path)?; let progress: &dyn Progress = &HgProgressBar::new("updating"); - hgerror_to_pyerr(py, update_from_null(&repo, to.into(), progress)) + + let res = with_sigint_wrapper(py, || { + update_from_null(&repo, to.into(), progress, num_cpus) + })?; + + hgerror_to_pyerr(py, res) } pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { @@ -41,7 +47,11 @@ "update_from_null", py_fn!( py, - update_from_null_fast_path(repo_path: PyObject, to: BaseRevision,) + update_from_null_fast_path( + repo_path: PyObject, + to: BaseRevision, + num_cpus: Option<usize> + ) ), )?;
--- a/rust/hg-cpython/src/utils.rs Mon Oct 21 12:58:40 2024 +0200 +++ b/rust/hg-cpython/src/utils.rs Thu Nov 14 16:45:23 2024 +0100 @@ -1,7 +1,7 @@ -use cpython::exc::ValueError; +use cpython::exc::{KeyboardInterrupt, ValueError}; use cpython::{ - ObjectProtocol, PyBytes, PyDict, PyErr, PyObject, PyResult, PyTuple, - Python, ToPyObject, + ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult, + PyTuple, Python, ToPyObject, }; use hg::config::Config; use hg::errors::HgError; @@ -50,6 +50,9 @@ cls.call(py, (msg,), None).ok().into_py_object(py), ) } + HgError::InterruptReceived => { + PyErr::new::<KeyboardInterrupt, _>(py, "") + } e => PyErr::new::<cpython::exc::RuntimeError, _>(py, e.to_string()), }) } @@ -64,6 +67,8 @@ /// Get a repository from a given [`PyObject`] path, and bubble up any error /// that comes up. pub fn repo_from_path(py: Python, repo_path: PyObject) -> Result<Repo, PyErr> { + // TODO make the Config a Python class and downcast it here, otherwise we + // lose CLI args and runtime overrides done in Python. let config = hgerror_to_pyerr(py, Config::load_non_repo().map_err(HgError::from))?; let py_bytes = &repo_path.extract::<PyBytes>(py)?; @@ -102,3 +107,38 @@ }) .map(Into::into) } + +/// Wrap a call to `func` so that Python's `SIGINT` handler is first stored, +/// then restored after the call to `func` and finally raised if +/// `func` returns a [`HgError::InterruptReceived`] +pub fn with_sigint_wrapper<R>( + py: Python, + func: impl Fn() -> Result<R, HgError>, +) -> PyResult<Result<R, HgError>> { + let signal_py_mod = py.import("signal")?; + let sigint_py_const = signal_py_mod.get(py, "SIGINT")?; + let old_handler = signal_py_mod.call( + py, + "getsignal", + PyTuple::new(py, &[sigint_py_const.clone_ref(py)]), + None, + )?; + let res = func(); + // Reset the old signal handler in Python because we've may have changed it + signal_py_mod.call( + py, + "signal", + PyTuple::new(py, &[sigint_py_const.clone_ref(py), old_handler]), + None, + )?; + if let Err(HgError::InterruptReceived) = res { + // Trigger the signal in Python + signal_py_mod.call( + py, + "raise_signal", + PyTuple::new(py, &[sigint_py_const]), + None, + )?; + } + Ok(res) +}
--- a/setup.py Mon Oct 21 12:58:40 2024 +0200 +++ b/setup.py Thu Nov 14 16:45:23 2024 +0100 @@ -375,9 +375,7 @@ eprint(r"/!\ Failed to retrieve current revision tags") return '' if numerictags: # tag(s) found - version = numerictags[-1] - if hgid.endswith('+'): # propagate the dirty status to the tag - version += '+' + return _version(tag=numerictags[-1], dirty=hgid.endswith('+')) else: # no tag found on the checked out revision ltagcmd = ['log', '--rev', 'wdir()', '--template', '{latesttag}'] ltag = sysstr(hg.run(ltagcmd)) @@ -396,9 +394,59 @@ "only(parents(),'%s')" % ltag, ] changessince = len(hg.run(changessincecmd).splitlines()) - version = '%s+hg%s.%s' % (ltag, changessince, hgid) - if version.endswith('+'): - version = version[:-1] + 'local' + time.strftime('%Y%m%d') + branch = hg.run(["branch"]).strip() + return _version( + tag=ltag, + branch=branch, + hgid=hgid.rstrip('+'), + changes_since=changessince, + dirty=hgid.endswith('+'), + ) + + +def _version( + tag: str, + branch: str = '', + hgid: str = '', + changes_since: int = 0, + dirty: bool = False, +): + """compute a version number from available information""" + version = tag + if changes_since > 0: + assert branch + if branch == b'stable': + post_nb = 0 + elif branch == b'default': + # we use 1 here to be greater than 0 to make sure change from + # default are considered newer than change on stable + post_nb = 1 + else: + # what is this branch ? probably a local variant ? + post_nb = 2 + + assert hgid + + # logic of the scheme + # - '.postX' to mark the version as "above" the tagged version + # X is 0 for stable, 1 for default, 2 for anything else + # - use '.devY' + # Y is the number of extra revision compared to the tag. So that + # revision with more change are "above" previous ones. + # - '+hg.NODEID.local.DATE' if there is any uncommitted changes. + version += '.post%d.dev%d+hg.%s' % (post_nb, changes_since, hgid) + if dirty: + version = version[:-1] + '.local.' + time.strftime('%Y%m%d') + # try to give warning early about bad version if possible + try: + from packaging.version import Version + + Version(version) + except ImportError: + pass + except ValueError as exc: + eprint(r"/!\ generated version is invalid") + eprint(r"/!\ error: %s" % exc) return version @@ -409,16 +457,23 @@ [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')] ) if 'tag' in kw: - version = kw['tag'] + version = _version(tag=kw['tag']) elif 'latesttag' in kw: - if 'changessincelatesttag' in kw: - version = ( - '%(latesttag)s+hg%(changessincelatesttag)s.%(node).12s' % kw - ) - else: - version = '%(latesttag)s+hg%(latesttagdistance)s.%(node).12s' % kw + distance = int(kw.get('changessincelatesttag', kw['latesttagdistance'])) + version = _version( + tag=kw['latesttag'], + branch=kw['branch'], + changes_since=distance, + hgid=kw['node'][:12], + ) else: - version = '0+hg' + kw.get('node', '')[:12] + version = _version( + tag='0', + branch='unknown-source', + changes_since=1, + hgid=kw.get('node', 'unknownid')[:12], + dirty=True, + ) elif os.path.exists('mercurial/__version__.py'): with open('mercurial/__version__.py') as f: data = f.read() @@ -464,6 +519,14 @@ description = "build translations (.mo files)" def run(self): + result = self._run() + if ( + not result + and os.environ.get('MERCURIAL_SETUP_FORCE_TRANSLATIONS') == '1' + ): + raise DistutilsExecError("failed to build translations") + + def _run(self): try: from shutil import which as find_executable except ImportError: @@ -475,12 +538,12 @@ "could not find msgfmt executable, no translations " "will be built" ) - return + return False podir = 'i18n' if not os.path.isdir(podir): self.warn("could not find %s/ directory" % podir) - return + return False join = os.path.join for po in os.listdir(podir): @@ -496,6 +559,7 @@ cmd.append('-c') self.mkpath(join('mercurial', modir)) self.make_file([pofile], mobuildfile, spawn, (cmd,)) + return True class hgdist(Distribution): @@ -1502,6 +1566,15 @@ env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir + # Wildy shooting in the dark to make sure rust-cpython use the right + # python + if not sys.executable: + msg = "Cannot determine which Python to compile Rust for" + raise RustCompilationError(msg) + env['PYTHON_SYS_EXECUTABLE'] = sys.executable + env['PYTHONEXECUTABLE'] = sys.executable + env['PYTHON'] = sys.executable + cargocmd = ['cargo', 'rustc', '--release'] rust_features = env.get("HG_RUST_FEATURES") @@ -1760,11 +1833,6 @@ if os.environ.get('PYOXIDIZER'): hgbuild.sub_commands.insert(0, ('build_hgextindex', None)) -if os.name == 'nt': - # Windows binary file versions for exe/dll files must have the - # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535 - setupversion = setupversion.split(r'+', 1)[0] - setup( version=setupversion, long_description=(
--- a/tests/run-tests.py Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/run-tests.py Thu Nov 14 16:45:23 2024 +0100 @@ -61,7 +61,6 @@ import shlex import shutil import signal -import site import socket import subprocess import sys @@ -448,6 +447,18 @@ help="skip tests listed in the specified blacklist file", ) selection.add_argument( + "--shard-total", + type=int, + default=None, + help="total number of shard to use (enable sharding)", + ) + selection.add_argument( + "--shard-index", + type=int, + default=None, + help="index of this shard [1-N]", + ) + selection.add_argument( "--changed", help="run tests that are changed in parent rev or working directory", ) @@ -886,6 +897,32 @@ if options.showchannels: options.nodiff = True + if options.shard_total is not None: + if options.shard_index is None: + parser.error("--shard-total requires --shard-index to be set") + + if options.shard_index is not None: + if options.shard_total is None: + parser.error("--shard-index requires --shard-total to be set") + elif options.shard_index <= 0: + msg = "--shard-index must be > 0 (%d)" + msg %= options.shard_index + parser.error(msg) + elif options.shard_index > options.shard_total: + msg = ( + "--shard-index must be <= than --shard-total (%d not in [1,%d])" + ) + msg %= (options.shard_index, options.shard_total) + parser.error(msg) + + if options.shard_total is not None and options.order_by_runtime: + msg = "cannot use --order-by-runtime when sharding" + parser.error(msg) + + if options.shard_total is not None and options.random: + msg = "cannot use --random when sharding" + parser.error(msg) + return options @@ -3158,7 +3195,11 @@ import statprof statprof.start() - result = self._run(testdescs) + result = self._run( + testdescs, + shard_index=options.shard_index, + shard_total=options.shard_total, + ) if options.profile_runner: statprof.stop() statprof.display() @@ -3167,7 +3208,7 @@ finally: os.umask(oldmask) - def _run(self, testdescs): + def _run(self, testdescs, shard_index=None, shard_total=None): testdir = getcwdb() # assume all tests in same folder for now if testdescs: @@ -3268,11 +3309,34 @@ else: self._installdir = os.path.join(self._hgtmp, b"install") - self._bindir = os.path.join(self._installdir, b"bin") + if WINDOWS: + # The wheel variant will install things in "Scripts". + # So we can as well always install things here. + self._bindir = os.path.join(self._installdir, b"Scripts") + else: + self._bindir = os.path.join(self._installdir, b"bin") self._hgcommand = b'hg' - if self.options.wheel: - suffix = _sys2bytes(site.USER_SITE[len(site.USER_BASE) + 1 :]) + if self.options.wheel and not WINDOWS: + # pip installing a wheel does not have an --install-lib flag + # so we have to guess where the file will be installed. + # + # In addition, that location is not really stable, so we are + # using awful symlink trrick later in `_installhg` + v_info = sys.version_info + suffix = os.path.join( + b"lib", + b"python%d.%d" % (v_info.major, v_info.minor), + b"site-packages", + ) + elif self.options.wheel and WINDOWS: + # for some reason, Windows use an even different scheme: + # + # <prefix>/lib/site-packages/ + suffix = os.path.join( + b"lib", + b"site-packages", + ) else: suffix = os.path.join(b"lib", b"python") self._pythondir = os.path.join(self._installdir, suffix) @@ -3281,7 +3345,13 @@ # a python script and feed it to python.exe. Legacy stdio is force # enabled by hg.exe, and this is a more realistic way to launch hg # anyway. - if WINDOWS and not self._hgcommand.endswith(b'.exe'): + # + # We do not do it when using wheels and they do not install a .exe. + if ( + WINDOWS + and not self.options.wheel + and not self._hgcommand.endswith(b'.exe') + ): self._hgcommand += b'.exe' real_hg = os.path.join(self._bindir, self._hgcommand) @@ -3445,6 +3515,14 @@ ) vlog("# Writing to directory", _bytes2sys(self._outputdir)) + if shard_total is not None: + slot = shard_index - 1 + testdescs = [ + t + for (idx, t) in enumerate(testdescs) + if (idx % shard_total == slot) + ] + try: return self._runtests(testdescs) or 0 finally: @@ -3720,10 +3798,7 @@ def _usecorrectpython(self): """Configure the environment to use the appropriate Python in tests.""" # Tests must use the same interpreter as us or bad things will happen. - if WINDOWS: - pyexe_names = [b'python', b'python3', b'python.exe'] - else: - pyexe_names = [b'python', b'python3'] + pyexe_names = [b'python', b'python3'] # os.symlink() is a thing with py3 on Windows, but it requires # Administrator rights. @@ -3764,7 +3839,8 @@ f.write(b'%s "$@"\n' % esc_executable) if WINDOWS: - # adjust the path to make sur the main python finds it own dll + # adjust the path to make sur the main python finds itself and + # its own dll path = os.environ['PATH'].split(os.pathsep) main_exec_dir = os.path.dirname(sysexecutable) extra_paths = [_bytes2sys(self._custom_bin_dir), main_exec_dir] @@ -3826,16 +3902,12 @@ wheel_path, b"--force", b"--ignore-installed", - b"--user", - b"--break-system-packages", + b"--prefix", + self._installdir, ] 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 + # windows does not have this flag apparently. + cmd.append(b"--break-system-packages") return cmd @@ -3909,14 +3981,18 @@ install_env.pop('HGWITHRUSTEXT', None) # setuptools requires install directories to exist. - def makedirs(p): - try: - os.makedirs(p) - except FileExistsError: - pass - - makedirs(self._pythondir) - makedirs(self._bindir) + os.makedirs(self._pythondir, exist_ok=True) + os.makedirs(self._bindir, exist_ok=True) + if self.options.wheel is not None and not WINDOWS: + # the wheel instalation location is not stable, so try to deal with + # that to funnel it back where we need its. + # + # (mostly deals with Debian shenanigans) + assert self._pythondir.endswith(b'site-packages') + lib_dir = os.path.dirname(self._pythondir) + dist_dir = os.path.join(lib_dir, b'dist-packages') + os.symlink(b'./site-packages', dist_dir) + os.symlink(b'.', os.path.join(self._installdir, b'local')) vlog("# Running", cmd) with open(installerrs, "wb") as logfile:
--- a/tests/test-bookmarks-pushpull.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-bookmarks-pushpull.t Thu Nov 14 16:45:23 2024 +0100 @@ -1155,20 +1155,21 @@ =============================================== #if b2-binary +(use `sh -c` as Windows struggle with the long argument) >>> with open('longname', 'w') as f: ... f.write('wat' * 100) and None - $ hg book `cat longname` - $ hg push -B `cat longname` ../unchanged-b + $ sh -c "hg book `cat longname`" + $ sh -c "hg push -B `cat longname` ../unchanged-b" pushing to ../unchanged-b searching for changes no changes found exporting bookmark (wat){100} (re) [1] - $ hg -R ../unchanged-b book --delete `cat longname` + $ sh -c "hg -R ../unchanged-b book --delete `cat longname`" Test again but forcing bundle2 exchange to make sure that doesn't regress. - $ hg push -B `cat longname` ../unchanged-b --config devel.legacy.exchange=bundle1 + $ sh -c "hg push -B `cat longname` ../unchanged-b --config devel.legacy.exchange=bundle1" pushing to ../unchanged-b searching for changes no changes found
--- a/tests/test-extdiff.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-extdiff.t Thu Nov 14 16:45:23 2024 +0100 @@ -167,6 +167,7 @@ [255] #endif +#if gui Test --per-file option for gui tool: $ DISPLAY=fake hg --config extdiff.gui.alabalaf=True alabalaf -c 6 --per-file --debug @@ -198,6 +199,7 @@ running '* diffing * *' in * (backgrounded) (glob) cleaning up temp directory [1] +#endif Test --per-file and --confirm options:
--- a/tests/test-git-interop.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-git-interop.t Thu Nov 14 16:45:23 2024 +0100 @@ -1,4 +1,4 @@ -#require pygit2 no-windows +#require pygit2 no-windows missing-correct-output Setup: $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
--- a/tests/test-hgrc.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-hgrc.t Thu Nov 14 16:45:23 2024 +0100 @@ -304,6 +304,10 @@ config error at $TESTTMP/.hg/hgrc:3: [broken [255] +XXX: This occasionally crashes with a bytes vs str problem when processing a +packet response, so disable it for now. + +#if missing-correct-output $ HGRCSKIPREPO=1 hg paths --config extensions.zeroconf= foo = $TESTTMP/bar - +#endif
--- a/tests/test-install.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-install.t Thu Nov 14 16:45:23 2024 +0100 @@ -1,3 +1,5 @@ + $ . "$RUNTESTDIR/helpers-testrepo.sh" + hg debuginstall $ hg debuginstall checking encoding (ascii)... @@ -212,13 +214,13 @@ Note: we use this weird path to run pip and hg to avoid platform differences, since it's bin on most platforms but Scripts on Windows. - $ ./installenv/*/pip install $TESTDIR/.. >> pip.log + $ (syshgenv; ./installenv/*/pip install $TESTDIR/.. >> pip.log) Failed building wheel for mercurial (?) WARNING: You are using pip version *; however, version * is available. (glob) (?) You should consider upgrading via the '$TESTTMP/installenv/bin/python* -m pip install --upgrade pip' command. (glob) (?) (?) [notice] A new release of pip is available: * -> * (glob) (?) - [notice] To update, run: python -m pip install --upgrade pip (?) + [notice] To update, run: * -m pip install --upgrade pip (glob) (?) $ ./installenv/*/hg debuginstall || cat pip.log checking encoding (ascii)... checking Python executable (*) (glob)
--- a/tests/test-paths.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-paths.t Thu Nov 14 16:45:23 2024 +0100 @@ -140,11 +140,16 @@ zeroconf wraps ui.configitems(), which shouldn't crash at least: +XXX: This occasionally crashes with 'TypeError: ord() expected string of length +1, but int found' when processing a packet response, so disable it for now. + +#if missing-correct-output $ hg paths --config extensions.zeroconf= dupe = $TESTTMP/b#tip dupe:pushurl = https://example.com/dupe expand = $TESTTMP/a/$SOMETHING/bar insecure = http://foo:***@example.com/ +#endif $ cd ..
--- a/tests/test-pushvars.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-pushvars.t Thu Nov 14 16:45:23 2024 +0100 @@ -1,8 +1,5 @@ Setup - $ PYTHONPATH=$TESTDIR/..:$PYTHONPATH - $ export PYTHONPATH - $ cat > $TESTTMP/pretxnchangegroup.sh << EOF > #!/bin/sh > env | grep -E "^HG_USERVAR_(DEBUG|BYPASS_REVIEW)" | sort
--- a/tests/test-racy-mutations.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-racy-mutations.t Thu Nov 14 16:45:23 2024 +0100 @@ -17,7 +17,7 @@ > [ -n "\${WAITLOCK_ANNOUNCE:-}" ] && touch "\${WAITLOCK_ANNOUNCE}" > f="\${WAITLOCK_FILE}" > start=\`date +%s\` - > timeout=5 + > timeout=20 > "$RUNTESTDIR_FORWARD_SLASH/testlib/wait-on-file" "\$timeout" "\$f" > if [ \$# -gt 1 ]; then > cat "\$@" @@ -65,19 +65,23 @@ > ) & Wait for the "editor" to actually start - $ sh "$RUNTESTDIR_FORWARD_SLASH/testlib/wait-on-file" 5 "${EDITOR_STARTED}" - + $ sh "$RUNTESTDIR_FORWARD_SLASH/testlib/wait-on-file" 20 "${EDITOR_STARTED}" Do a concurrent edition $ cd ../racing-client $ touch ../pre-race - $ sleep 1 + $ sleep 10 $ echo bar > bar $ hg --repository ../racing-client commit -qAm 'r2 (bar)' bar $ hg --repository ../racing-client debugrevlogindex -c rev linkrev nodeid p1 p2 0 0 222799e2f90b 000000000000 000000000000 1 1 6f124f6007a0 222799e2f90b 000000000000 + $ hg --repository ../racing-client debugrevlogindex -m + rev linkrev nodeid p1 p2 + 0 0 7b7020262a56 000000000000 000000000000 + 1 1 ad3fe36d86d9 7b7020262a56 000000000000 + We simulate an network FS race by overwriting raced repo content with the new content of the files changed in the racing repository @@ -102,6 +106,15 @@ 0 0 222799e2f90b 000000000000 000000000000 1 1 6f124f6007a0 222799e2f90b 000000000000 2 1 ac80e6205bb2 222799e2f90b 000000000000 + +TODO: Figure out why the middle entry is missing on Windows. + $ hg debugrevlogindex -m + rev linkrev nodeid p1 p2 + 0 0 7b7020262a56 000000000000 000000000000 + 1 1 ad3fe36d86d9 7b7020262a56 000000000000 (no-windows !) + 2 1 d93163bb8ce3 7b7020262a56 000000000000 (no-windows !) + 1 1 d93163bb8ce3 7b7020262a56 000000000000 (windows !) + #endif #if fail-if-detected @@ -116,10 +129,14 @@ rev linkrev nodeid p1 p2 0 0 222799e2f90b 000000000000 000000000000 1 1 6f124f6007a0 222799e2f90b 000000000000 (missing-correct-output !) + And, because of transactions, there's none in the manifestlog either. + +TODO: Figure out why this is different on Windows. $ hg debugrevlogindex -m rev linkrev nodeid p1 p2 0 0 7b7020262a56 000000000000 000000000000 - 1 1 ad3fe36d86d9 7b7020262a56 000000000000 + 1 1 ad3fe36d86d9 7b7020262a56 000000000000 (no-windows !) + 1 1 ad3fe36d86d9 7b7020262a56 000000000000 (missing-correct-output windows !) #endif
--- a/tests/test-run-tests.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-run-tests.t Thu Nov 14 16:45:23 2024 +0100 @@ -1762,6 +1762,7 @@ # Ran 2 tests, 0 skipped, 1 failed. python hash seed: * (glob) [1] + $ rm output/* Test TESTCASE variable @@ -2087,3 +2088,34 @@ 3.* (glob) $ ./test-py.py 3.* (glob) + +Test sharding +============= + + $ rt --shard-index 1 + usage: run-tests.py [options] [tests] + run-tests.py: error: --shard-index requires --shard-total to be set + [2] + $ rt --shard-total 15 + usage: run-tests.py [options] [tests] + run-tests.py: error: --shard-total requires --shard-index to be set + [2] + $ rt --shard-index -2 --shard-total 15 + usage: run-tests.py [options] [tests] + run-tests.py: error: --shard-index must be > 0 (-2) + [2] + $ rt --shard-index 10 --shard-total 5 + usage: run-tests.py [options] [tests] + run-tests.py: error: --shard-index must be <= than --shard-total (10 not in [1,5]) + [2] + $ rt --list-tests --shard-index 1 --shard-total 5 + test-cases-abc.t#A + test-cases-advanced-cases.t#casewith_-.chars + test-config-opt.t + $ rt --shard-index 6 --shard-total 5 + usage: run-tests.py [options] [tests] + run-tests.py: error: --shard-index must be <= than --shard-total (6 not in [1,5]) + [2] + $ rt --list-tests --shard-index 5 --shard-total 5 + test-cases-advanced-cases.t#case-with-dashes + test-conditional-matching.t#foo
--- a/tests/test-status-color.t Mon Oct 21 12:58:40 2024 +0200 +++ b/tests/test-status-color.t Thu Nov 14 16:45:23 2024 +0100 @@ -399,9 +399,16 @@ color coding of error message without curses $ echo 'raise ImportError' > curses.py +#if windows + $ PYTHONPATH="`pwd`;$PYTHONPATH" hg unknowncommand > /dev/null + hg: unknown command 'unknowncommand' + (use 'hg help' for a list of commands) + [10] +#else $ PYTHONPATH=`pwd`:$PYTHONPATH hg unknowncommand > /dev/null hg: unknown command 'unknowncommand' (use 'hg help' for a list of commands) [10] +#endif $ cd ..