diff --git a/contrib/heptapod-ci.yml b/contrib/heptapod-ci.yml new file mode 100644 --- /dev/null +++ b/contrib/heptapod-ci.yml @@ -0,0 +1,75 @@ +image: octobus/ci-mercurial-core + +# The runner made a clone as root. +# We make a new clone owned by user used to run the step. +before_script: + - hg clone . /tmp/mercurial-ci/ --noupdate + - hg -R /tmp/mercurial-ci/ update `hg log --rev '.' --template '{node}'` + - cd /tmp/mercurial-ci/ + - (cd tests; ls -1 test-check-*.*) > /tmp/check-tests.txt + +variables: + PYTHON: python + +.runtests_template: &runtests + script: + - cd tests/ + - echo "python used, $PYTHON" + - echo "$RUNTEST_ARGS" + - $PYTHON run-tests.py --color=always $RUNTEST_ARGS + +checks-py2: + <<: *runtests + variables: + RUNTEST_ARGS: "--time --test-list /tmp/check-tests.txt" + +checks-py3: + <<: *runtests + variables: + RUNTEST_ARGS: "--time --test-list /tmp/check-tests.txt" + PYTHON: python3 + +rust-cargo-test-py2: &rust_cargo_test + script: + - echo "python used, $PYTHON" + - make rust-tests + +rust-cargo-test-py3: + <<: *rust_cargo_test + variables: + PYTHON: python3 + +test-py2: + <<: *runtests + variables: + RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt" + +test-py3: + <<: *runtests + variables: + RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt" + PYTHON: python3 + +test-py2-pure: + <<: *runtests + variables: + RUNTEST_ARGS: "--pure --blacklist /tmp/check-tests.txt" + +test-py3-pure: + <<: *runtests + variables: + RUNTEST_ARGS: "--pure --blacklist /tmp/check-tests.txt" + PYTHON: python3 + +test-py2-rust: + <<: *runtests + variables: + HGWITHRUSTEXT: cpython + RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt" + +test-py3-rust: + <<: *runtests + variables: + HGWITHRUSTEXT: cpython + RUNTEST_ARGS: "--blacklist /tmp/check-tests.txt" + PYTHON: python3 diff --git a/contrib/packaging/hgpackaging/wix.py b/contrib/packaging/hgpackaging/wix.py --- a/contrib/packaging/hgpackaging/wix.py +++ b/contrib/packaging/hgpackaging/wix.py @@ -343,7 +343,9 @@ def build_installer( dist_dir = source_dir / 'dist' wix_dir = source_dir / 'contrib' / 'packaging' / 'wix' - requirements_txt = 'requirements_win32.txt' + requirements_txt = ( + source_dir / 'contrib' / 'packaging' / 'requirements_win32.txt' + ) build_py2exe( source_dir, diff --git a/hgext/zeroconf/__init__.py b/hgext/zeroconf/__init__.py --- a/hgext/zeroconf/__init__.py +++ b/hgext/zeroconf/__init__.py @@ -35,6 +35,7 @@ from mercurial import ( extensions, hg, pycompat, + rcutil, ui as uimod, ) from mercurial.hgweb import server as servermod @@ -144,7 +145,8 @@ def zc_create_server(create_server, ui, prefix = app.ui.config(b"web", b"prefix", b"").strip(b'/') + b'/' for repo, path in repos: u = app.ui.copy() - u.readconfig(os.path.join(path, b'.hg', b'hgrc')) + if rcutil.use_repo_hgrc(): + u.readconfig(os.path.join(path, b'.hg', b'hgrc')) name = os.path.basename(repo) path = (prefix + repo).strip(b'/') desc = u.config(b'web', b'description') diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -1014,8 +1014,8 @@ class bundlepart(object): self.mandatory = mandatory def __repr__(self): - cls = b"%s.%s" % (self.__class__.__module__, self.__class__.__name__) - return b'<%s object at %x; id: %s; type: %s; mandatory: %s>' % ( + cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) + return '<%s object at %x; id: %s; type: %s; mandatory: %s>' % ( cls, id(self), self.id, diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py --- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -37,6 +37,7 @@ from . import ( hook, profiling, pycompat, + rcutil, registrar, scmutil, ui as uimod, @@ -902,17 +903,20 @@ def _getlocal(ui, rpath, wd=None): _(b"error getting current working directory: %s") % encoding.strtolocal(e.strerror) ) + path = cmdutil.findrepo(wd) or b"" if not path: lui = ui else: lui = ui.copy() - lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path) + if rcutil.use_repo_hgrc(): + lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path) if rpath: path = lui.expandpath(rpath) lui = ui.copy() - lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path) + if rcutil.use_repo_hgrc(): + lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path) return path, lui diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py +++ b/mercurial/hgweb/hgwebdir_mod.py @@ -35,6 +35,7 @@ from .. import ( pathutil, profiling, pycompat, + rcutil, registrar, scmutil, templater, @@ -192,11 +193,12 @@ def rawindexentries(ui, repos, req, subd continue u = ui.copy() - try: - u.readconfig(os.path.join(path, b'.hg', b'hgrc')) - except Exception as e: - u.warn(_(b'error reading %s/.hg/hgrc: %s\n') % (path, e)) - continue + if rcutil.use_repo_hgrc(): + try: + u.readconfig(os.path.join(path, b'.hg', b'hgrc')) + except Exception as e: + u.warn(_(b'error reading %s/.hg/hgrc: %s\n') % (path, e)) + continue def get(section, name, default=uimod._unset): return u.config(section, name, default, untrusted=True) diff --git a/mercurial/linelog.py b/mercurial/linelog.py --- a/mercurial/linelog.py +++ b/mercurial/linelog.py @@ -255,7 +255,7 @@ class linelog(object): ) def __repr__(self): - return b'' % ( + return '' % ( hex(id(self)), self._maxrev, len(self._program), diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -53,6 +53,7 @@ from . import ( phases, pushkey, pycompat, + rcutil, repoview, revset, revsetlang, @@ -676,7 +677,7 @@ def loadhgrc(ui, wdirvfs, hgvfs, require configs are loaded. For example, an extension may wish to pull in configs from alternate files or sources. """ - if b'HGRCSKIPREPO' in encoding.environ: + if not rcutil.use_repo_hgrc(): return False try: ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base) diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -21,6 +21,7 @@ from .node import ( ) from .pycompat import getattr from . import ( + encoding, error, mdiff, pathutil, @@ -868,9 +869,10 @@ class treemanifest(object): self._loadalllazy() return not self._dirs or all(m._isempty() for m in self._dirs.values()) + @encoding.strmethod def __repr__(self): return ( - b'' + b'' % ( self._dir, hex(self._node), diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -1090,6 +1090,7 @@ class recordhunk(object): def filename(self): return self.header.filename() + @encoding.strmethod def __repr__(self): return b'' % (self.filename(), self.fromline) diff --git a/mercurial/rcutil.py b/mercurial/rcutil.py --- a/mercurial/rcutil.py +++ b/mercurial/rcutil.py @@ -112,3 +112,8 @@ def defaultpagerenv(): intended to be set before starting a pager. ''' return {b'LESS': b'FRX', b'LV': b'-c'} + + +def use_repo_hgrc(): + """True if repositories `.hg/hgrc` config should be read""" + return b'HGRCSKIPREPO' not in encoding.environ diff --git a/mercurial/worker.py b/mercurial/worker.py --- a/mercurial/worker.py +++ b/mercurial/worker.py @@ -65,6 +65,41 @@ def _numworkers(ui): return min(max(countcpus(), 4), 32) +if pycompat.ispy3: + + class _blockingreader(object): + def __init__(self, wrapped): + self._wrapped = wrapped + + def __getattr__(self, attr): + return getattr(self._wrapped, attr) + + # issue multiple reads until size is fulfilled + def read(self, size=-1): + if size < 0: + return self._wrapped.readall() + + buf = bytearray(size) + view = memoryview(buf) + pos = 0 + + while pos < size: + ret = self._wrapped.readinto(view[pos:]) + if not ret: + break + pos += ret + + del view + del buf[pos:] + return buf + + +else: + + def _blockingreader(wrapped): + return wrapped + + if pycompat.isposix or pycompat.iswindows: _STARTUP_COST = 0.01 # The Windows worker is thread based. If tasks are CPU bound, threads @@ -226,7 +261,7 @@ def _posixworker(ui, func, staticargs, a selector = selectors.DefaultSelector() for rfd, wfd in pipes: os.close(wfd) - selector.register(os.fdopen(rfd, 'rb'), selectors.EVENT_READ) + selector.register(os.fdopen(rfd, 'rb', 0), selectors.EVENT_READ) def cleanup(): signal.signal(signal.SIGINT, oldhandler) @@ -240,7 +275,7 @@ def _posixworker(ui, func, staticargs, a while openpipes > 0: for key, events in selector.select(): try: - res = util.pickle.load(key.fileobj) + res = util.pickle.load(_blockingreader(key.fileobj)) if hasretval and res[0]: retval.update(res[1]) else: diff --git a/tests/test-hgrc.t b/tests/test-hgrc.t --- a/tests/test-hgrc.t +++ b/tests/test-hgrc.t @@ -271,3 +271,39 @@ Test we can skip the user configuration $ HGRCSKIPREPO=1 hg path foo = $TESTTMP/bar + $ cat >> .hg/hgrc < [broken + > EOF + + $ hg path + hg: parse error at $TESTTMP/.hg/hgrc:3: [broken + [255] + $ HGRCSKIPREPO=1 hg path + foo = $TESTTMP/bar + +Check that hgweb respect HGRCSKIPREPO=1 + + $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log + hg: parse error at $TESTTMP/.hg/hgrc:3: [broken + [255] + $ test -f hg.pid && (cat hg.pid >> $DAEMON_PIDS) + [1] + $ killdaemons.py + $ test -f access.log && cat access.log + [1] + $ test -f errors.log && cat errors.log + [1] + + $ HGRCSKIPREPO=1 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log + $ cat hg.pid >> $DAEMON_PIDS + $ killdaemons.py + $ cat access.log + $ cat errors.log + +Check that zeroconf respect HGRCSKIPREPO=1 + + $ hg paths --config extensions.zeroconf= + hg: parse error at $TESTTMP/.hg/hgrc:3: [broken + [255] + $ HGRCSKIPREPO=1 hg paths --config extensions.zeroconf= + foo = $TESTTMP/bar diff --git a/tests/test-worker.t b/tests/test-worker.t --- a/tests/test-worker.t +++ b/tests/test-worker.t @@ -131,4 +131,35 @@ Workers should not do cleanups in all ca abort: known exception [255] +Do not crash on partially read result + + $ cat > $TESTTMP/detecttruncated.py < from __future__ import absolute_import + > import os + > import sys + > import time + > sys.unraisablehook = lambda x: None + > oldwrite = os.write + > def splitwrite(fd, string): + > ret = oldwrite(fd, string[:9]) + > if ret == 9: + > time.sleep(0.1) + > ret += oldwrite(fd, string[9:]) + > return ret + > os.write = splitwrite + > EOF + + $ hg --config "extensions.t=$abspath" --config worker.numcpus=8 --config \ + > "extensions.d=$TESTTMP/detecttruncated.py" test 100000.0 + start + run + run + run + run + run + run + run + run + done + #endif