summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJames McCoy <jamessan@debian.org>2020-04-10 08:28:37 -0400
committerJames McCoy <jamessan@debian.org>2020-04-10 08:28:37 -0400
commitf04f369300121ce8d8e66eb5aaeb2926e02880e4 (patch)
tree2020cc3a965e09afeae1348ece5300705716728e /tools
parentff0dcb36b05eb662b51423774a4502bc04378f69 (diff)
New upstream version 1.14.0~rc2
Diffstat (limited to 'tools')
-rwxr-xr-xtools/backup/hot-backup.py.in2
-rwxr-xr-xtools/buildbot/slaves/bb-openbsd/svncheck-bindings.sh3
-rwxr-xr-xtools/buildbot/slaves/bb-openbsd/svnclean.sh2
-rwxr-xr-xtools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh46
-rwxr-xr-xtools/buildbot/slaves/svn-x64-macosx/svnbuild.sh7
-rwxr-xr-xtools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh9
-rwxr-xr-xtools/buildbot/slaves/svn-x64-macosx/svncheck.sh5
-rw-r--r--tools/client-side/bash_completion5
-rw-r--r--tools/client-side/svn-mergeinfo-normalizer/log.c2
-rw-r--r--tools/client-side/svn-mergeinfo-normalizer/logic.c10
-rw-r--r--tools/client-side/svn-mergeinfo-normalizer/wc_mergeinfo.c2
-rw-r--r--tools/client-side/svnconflict/svnconflict.c2
-rwxr-xr-xtools/dev/contribulyze.py12
-rw-r--r--tools/dev/svnmover/scanlog.c2
-rw-r--r--tools/dev/unix-build/Makefile.svn97
-rw-r--r--tools/dev/unix-build/README2
-rwxr-xr-xtools/dev/which-error.py2
-rwxr-xr-xtools/dist/backport.pl25
-rw-r--r--tools/dist/backport/merger.py6
-rw-r--r--tools/dist/backport/status.py10
-rw-r--r--tools/dist/backport_tests.py2
-rwxr-xr-xtools/dist/checksums.py2
-rwxr-xr-xtools/dist/create-minor-release-branch.py328
-rw-r--r--tools/dist/release-lines.yaml30
-rwxr-xr-xtools/dist/release.py620
-rw-r--r--tools/dist/security/_gnupg.py4
-rw-r--r--tools/dist/templates/STATUS.ezt24
-rw-r--r--tools/dist/templates/download.ezt6
-rw-r--r--tools/dist/templates/rc-release-ann.ezt4
-rw-r--r--tools/dist/templates/release-notes-lts.ezt251
-rw-r--r--tools/dist/templates/release-notes.ezt251
-rw-r--r--tools/dist/templates/stable-release-ann.ezt4
-rwxr-xr-xtools/examples/svnlook.py2
-rwxr-xr-xtools/examples/walk-config-auth.py2
-rwxr-xr-xtools/hook-scripts/commit-access-control.pl.in2
-rw-r--r--tools/hook-scripts/mailer/mailer.conf.example7
-rwxr-xr-xtools/hook-scripts/mailer/mailer.py192
-rwxr-xr-xtools/hook-scripts/svn2feed.py2
-rwxr-xr-xtools/hook-scripts/svnperms.py2
-rwxr-xr-xtools/hook-scripts/validate-files.py34
-rwxr-xr-xtools/server-side/fsfs-reshard.py2
-rwxr-xr-xtools/server-side/svn-backup-dumps.py64
42 files changed, 1412 insertions, 674 deletions
diff --git a/tools/backup/hot-backup.py.in b/tools/backup/hot-backup.py.in
index f497ce2..cab602a 100755
--- a/tools/backup/hot-backup.py.in
+++ b/tools/backup/hot-backup.py.in
@@ -28,7 +28,7 @@
# under the License.
# ====================================================================
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/backup/hot-backup.py.in $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/backup/hot-backup.py.in $
# $LastChangedDate: 2015-12-21 15:02:31 +0000 (Mon, 21 Dec 2015) $
# $LastChangedBy: danielsh $
# $LastChangedRevision: 1721179 $
diff --git a/tools/buildbot/slaves/bb-openbsd/svncheck-bindings.sh b/tools/buildbot/slaves/bb-openbsd/svncheck-bindings.sh
index e68775b..3a15239 100755
--- a/tools/buildbot/slaves/bb-openbsd/svncheck-bindings.sh
+++ b/tools/buildbot/slaves/bb-openbsd/svncheck-bindings.sh
@@ -27,6 +27,7 @@ branch="${url##*/}"
export MALLOC_OPTIONS=S
(cd .. && gmake BRANCH="$branch" THREADING="no" JAVA="no" svn-check-bindings)
grep -q "^Result: PASS$" tests.log.bindings.pl || exit 1
-grep -q "^OK$" tests.log.bindings.py || exit 1
+grep -q "^OK$" tests.log.bindings.py || \
+ grep -q "^OK (skipped=[0-9]" tests.log.bindings.py || exit 1
grep -q ", 0 failures, 0 errors" tests.log.bindings.rb || exit 1
exit 0
diff --git a/tools/buildbot/slaves/bb-openbsd/svnclean.sh b/tools/buildbot/slaves/bb-openbsd/svnclean.sh
index a2f86e0..553e0f4 100755
--- a/tools/buildbot/slaves/bb-openbsd/svnclean.sh
+++ b/tools/buildbot/slaves/bb-openbsd/svnclean.sh
@@ -22,11 +22,13 @@
set -e
set -x
+svn cleanup ../build
(test -h ../svn-trunk || ln -s build ../svn-trunk)
for i in $(jot - 6 12); do
(test -h ../svn-1.${i}.x || ln -s build ../svn-1.${i}.x)
done
lastchangedrev="$(svn info --show-item=last-changed-revision ../../unix-build/Makefile.svn)"
+svn cleanup ../../unix-build
svn update ../../unix-build
newlastchangedrev="$(svn info --show-item=last-changed-revision ../../unix-build/Makefile.svn)"
(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile)
diff --git a/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh b/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh
index 284f320..1861644 100755
--- a/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh
+++ b/tools/buildbot/slaves/svn-x64-macosx/svnbuild-bindings.sh
@@ -24,17 +24,51 @@ scripts=$(cd $(dirname "$0") && pwd)
. ${scripts}/setenv.sh
+# Parse arguments to find out which bindings we should build
+if [ -z "$1" ]; then
+ use_python3=false
+ build_swig_py=true
+ build_swig_pl=true
+ build_swig_rb=true
+ build_javahl=true
+else
+ use_python3=false
+ build_swig_py=false
+ build_swig_pl=false
+ build_swig_rb=false
+ build_javahl=false
+
+ while [ ! -z "$1" ]; do
+ case "$1" in
+ python3) use_python3=true;;
+ swig-py) build_swig_py=true;;
+ swig-pl) build_swig_pl=true;;
+ swig-rb) build_swig_rb=true;;
+ javahl) build_javahl=true;;
+ *) exit 1;;
+ esac
+ shift
+ done
+fi
+
+${use_python3} \
+ && test -n "${SVNBB_PYTHON3ENV}" \
+ && . ${SVNBB_PYTHON3ENV}/bin/activate \
+ && export PYTHON="$(which python)"
+
#
-# Step 4: build bindings
+# Step 1: build bindings
#
build_bindings() {
echo "============ make $1"
cd ${absbld}
- make $1
+ make $1 || exit 1
}
-build_bindings swig-py
-build_bindings swig-pl
-build_bindings swig-rb
-build_bindings javahl
+${build_swig_py} && build_bindings swig-py
+${build_swig_pl} && build_bindings swig-pl
+${build_swig_rb} && build_bindings swig-rb
+${build_javahl} && build_bindings javahl
+
+exit 0
diff --git a/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh b/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh
index 8d70769..0918dbc 100755
--- a/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh
+++ b/tools/buildbot/slaves/svn-x64-macosx/svnbuild.sh
@@ -54,6 +54,13 @@ else
parallel=${SVNBB_PARALLEL}
fi
+# An optional parameter tells us if this build should use Python 3.
+if [ "$3" = "python3" ]; then
+ test -n "${SVNBB_PYTHON3ENV}" \
+ && . ${SVNBB_PYTHON3ENV}/bin/activate \
+ && export PYTHON="$(which python)"
+fi
+
#
# Step 0: Create a directory for the test log files
#
diff --git a/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh b/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh
index 4810408..f1fca22 100755
--- a/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh
+++ b/tools/buildbot/slaves/svn-x64-macosx/svncheck-bindings.sh
@@ -37,6 +37,7 @@ scripts=$(cd $(dirname "$0") && pwd)
. ${scripts}/setenv.sh
# Parse arguments to find out which tests we should run
+use_python3=false
check_swig_py=false
check_swig_pl=false
check_swig_rb=false
@@ -44,15 +45,21 @@ check_javahl=false
while [ ! -z "$1" ]; do
case "$1" in
+ python3) use_python3=true;;
swig-py) check_swig_py=true;;
swig-pl) check_swig_pl=true;;
swig-rb) check_swig_rb=true;;
javahl) check_javahl=true;;
- *) exit 1;;
+ *) exit 1;;
esac
shift
done
+${use_python3} \
+ && test -n "${SVNBB_PYTHON3ENV}" \
+ && . ${SVNBB_PYTHON3ENV}/bin/activate \
+ && export PYTHON="$(which python)"
+
${check_swig_py} && run_tests swig-py
${check_swig_pl} && run_tests swig-pl
${check_swig_rb} && run_tests swig-rb
diff --git a/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh b/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh
index 9465d3f..f276e2d 100755
--- a/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh
+++ b/tools/buildbot/slaves/svn-x64-macosx/svncheck.sh
@@ -98,7 +98,10 @@ while [ ! -z "$1" ]; do
shift
done
-${use_python3} && test -n "${SVNBB_PYTHON3ENV}" && . ${SVNBB_PYTHON3ENV}/bin/activate
+${use_python3} \
+ && test -n "${SVNBB_PYTHON3ENV}" \
+ && . ${SVNBB_PYTHON3ENV}/bin/activate \
+ && export PYTHON="$(which python)"
${check_local} && check_tests local
${check_svn} && check_tests svn
diff --git a/tools/client-side/bash_completion b/tools/client-side/bash_completion
index 44d15bb..2d06d8f 100644
--- a/tools/client-side/bash_completion
+++ b/tools/client-side/bash_completion
@@ -1137,7 +1137,7 @@ _svnadmin ()
cur=${COMP_WORDS[COMP_CWORD]}
# Possible expansions, without pure-prefix abbreviations such as "h".
- cmds='crashtest create delrevprop deltify dump dump-revprops freeze \
+ cmds='build-repcache crashtest create delrevprop deltify dump dump-revprops freeze \
help hotcopy info list-dblogs list-unused-dblogs \
load load-revprops lock lslocks lstxns pack recover rev-size rmlocks \
rmtxns setlog setrevprop setuuid unlock upgrade verify --version'
@@ -1163,6 +1163,9 @@ _svnadmin ()
cmdOpts=
case ${COMP_WORDS[1]} in
+ build-repcache)
+ cmdOpts="-r --revision -q --quiet -M --memory-cache-size"
+ ;;
create)
cmdOpts="--bdb-txn-nosync --bdb-log-keep --config-dir \
--fs-type --compatible-version"
diff --git a/tools/client-side/svn-mergeinfo-normalizer/log.c b/tools/client-side/svn-mergeinfo-normalizer/log.c
index c73d101..5eb609a 100644
--- a/tools/client-side/svn-mergeinfo-normalizer/log.c
+++ b/tools/client-side/svn-mergeinfo-normalizer/log.c
@@ -866,7 +866,7 @@ svn_min__get_copies(svn_min__log_t *log,
if (copies[idx]->copyfrom_revision <= start_rev)
APR_ARRAY_PUSH(result, const svn_min__copy_t *) = copies[idx];
}
-
+
/* Find all parent copies. */
while (!svn_fspath__is_root(to_find->copyfrom_path,
strlen(to_find->copyfrom_path)))
diff --git a/tools/client-side/svn-mergeinfo-normalizer/logic.c b/tools/client-side/svn-mergeinfo-normalizer/logic.c
index 4911c08..ae2d09d 100644
--- a/tools/client-side/svn-mergeinfo-normalizer/logic.c
+++ b/tools/client-side/svn-mergeinfo-normalizer/logic.c
@@ -436,7 +436,7 @@ find_surviving_copy(svn_min__log_t *log,
survivor = apr_pstrdup(result_pool, copy_target);
}
}
-
+
svn_pool_destroy(iterpool);
return survivor;
@@ -488,7 +488,7 @@ find_surviving_copies(apr_array_header_t *survivors,
copy_target);
}
}
-
+
svn_pool_destroy(iterpool);
}
@@ -1351,7 +1351,7 @@ progress_string(const progress_t *progress,
/* Depending on the options in OPT_STATE, print the header to be shown
* before processing the m/i at REL_PATH relative to the parent mergeinfo
- * at PARENT_PATH. If there is no parent m/i, RELPATH is empty.
+ * at PARENT_PATH. If there is no parent m/i, RELPATH is empty.
* Use SCRATCH_POOL temporary allocations.*/
static svn_error_t *
show_elision_header(const char *parent_path,
@@ -1651,7 +1651,7 @@ eliminate_subpaths(apr_array_header_t *paths)
}
/* If enabled by OPT_STATE, show the list of missing paths encountered by
- * LOOKUP and use LOG to determine their fate. LOG may be NULL.
+ * LOOKUP and use LOG to determine their fate. LOG may be NULL.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
show_obsoletes_summary(svn_min__branch_lookup_t *lookup,
@@ -1702,7 +1702,7 @@ show_obsoletes_summary(svn_min__branch_lookup_t *lookup,
SVN_INVALID_REVNUM,
creation_rev, iterpool);
find_surviving_copies(surviving_copies, log, path,
- SVN_IS_VALID_REVNUM(deletion_rev)
+ SVN_IS_VALID_REVNUM(deletion_rev)
? deletion_rev - 1
: deletion_rev,
creation_rev,
diff --git a/tools/client-side/svn-mergeinfo-normalizer/wc_mergeinfo.c b/tools/client-side/svn-mergeinfo-normalizer/wc_mergeinfo.c
index edc2208..a055844 100644
--- a/tools/client-side/svn-mergeinfo-normalizer/wc_mergeinfo.c
+++ b/tools/client-side/svn-mergeinfo-normalizer/wc_mergeinfo.c
@@ -130,7 +130,7 @@ get_urls(void *baton,
apr_pool_t *target_pool = apr_hash_pool_get(mi->mergeinfo);
const char *rel_path = svn_uri_skip_ancestor(info->repos_root_URL,
info->URL, pool);
-
+
mi->url = apr_pstrdup(target_pool, info->URL);
mi->fs_path = svn_fspath__canonicalize(rel_path, target_pool);
diff --git a/tools/client-side/svnconflict/svnconflict.c b/tools/client-side/svnconflict/svnconflict.c
index afbedf4..dcc7576 100644
--- a/tools/client-side/svnconflict/svnconflict.c
+++ b/tools/client-side/svnconflict/svnconflict.c
@@ -373,7 +373,7 @@ svnconflict_list(apr_getopt_t *os, void *baton, apr_pool_t *pool)
for (i = 0; i < props_conflicted->nelts; i++)
{
- const char *propname = APR_ARRAY_IDX(props_conflicted, i, const char *);
+ const char *propname = APR_ARRAY_IDX(props_conflicted, i, const char *);
SVN_ERR(svn_cmdline_printf(pool, "prop-conflict: %s\n", propname));
}
diff --git a/tools/dev/contribulyze.py b/tools/dev/contribulyze.py
index 8afc608..6789056 100755
--- a/tools/dev/contribulyze.py
+++ b/tools/dev/contribulyze.py
@@ -56,6 +56,7 @@
# a lot easier to whip up for straight 'svn log' output. I'd have no
# objection to it being rewritten to take XML input.
+import functools
import os
import sys
import re
@@ -256,6 +257,9 @@ class Contributor(object):
else:
return 0 - result
+ def sort_key(self):
+ return (self.is_full_committer, self.score(), self.big_name())
+
@staticmethod
def parse(name):
"""Parse NAME, which can be
@@ -398,7 +402,7 @@ class Contributor(object):
out.write('</table>\n\n')
out.write('</div>\n\n')
- sorted_logs = sorted(unique_logs.keys())
+ sorted_logs = sorted(unique_logs.keys(), key=LogMessage.sort_key)
for log in sorted_logs:
out.write('<hr />\n')
out.write('<div class="h3" id="%s" title="%s">\n' % (log.revision,
@@ -490,6 +494,9 @@ class LogMessage(object):
if a < b: return 1
else: return 0
+ def sort_key(self):
+ return int(self.revision[1:])
+
def __str__(self):
s = '=' * 15
header = ' LOG: %s | %s ' % (self.revision, self.committer)
@@ -661,7 +668,8 @@ def drop(revision_url_pattern):
# sort by number of contributions, so the most active people appear at
# the top -- that way we know whom to look at first for commit access
# proposals.
- sorted_contributors = sorted(Contributor.all_contributors.values())
+ sorted_contributors = sorted(Contributor.all_contributors.values(),
+ key = Contributor.sort_key)
for c in sorted_contributors:
if c not in seen_contributors:
if c.score() > 0:
diff --git a/tools/dev/svnmover/scanlog.c b/tools/dev/svnmover/scanlog.c
index ebf8c53..e46d037 100644
--- a/tools/dev/svnmover/scanlog.c
+++ b/tools/dev/svnmover/scanlog.c
@@ -136,7 +136,7 @@ typedef struct scan_moves_log_receiver_baton {
* rB: mv b->c
* rC: mv c->d
* we map each revision number to all moves which happened in the
- * revision, which looks as follows:
+ * revision, which looks as follows:
* rA : [(rA, x->z), (rA, a->b)]
* rB : [(rB, b->c)]
* rC : [(rC, c->d)]
diff --git a/tools/dev/unix-build/Makefile.svn b/tools/dev/unix-build/Makefile.svn
index b00a074..b92c076 100644
--- a/tools/dev/unix-build/Makefile.svn
+++ b/tools/dev/unix-build/Makefile.svn
@@ -112,7 +112,8 @@ SQLITE_VER = 3160200
LIBMAGIC_VER = 5.30
RUBY_VER = 2.4.4
BZ2_VER = 1.0.6
-PYTHON_VER = 2.7.13
+PYTHON_VER = 3.7.5
+PY3C_VER = 1.1
JUNIT_VER = 4.10
GETTEXT_VER = 0.19.8.1
LZ4_VER = 1.7.5
@@ -129,6 +130,7 @@ LIBMAGIC_DIST = file-$(LIBMAGIC_VER).tar.gz
RUBY_DIST = ruby-$(RUBY_VER).tar.gz
BZ2_DIST = bzip2-$(BZ2_VER).tar.gz
PYTHON_DIST = Python-$(PYTHON_VER).tgz
+PY3C_DIST = py3c-$(PY3C_VER).tar.gz
JUNIT_DIST = junit-${JUNIT_VER}.jar
GETTEXT_DIST = gettext-$(GETTEXT_VER).tar.gz
LZ4_DIST = lz4-$(LZ4_VER).tar.gz
@@ -144,7 +146,8 @@ SHA256_${SQLITE_DIST} = 65cc0c3e9366f50c0679c5ccd31432cea894bc4a3e8947dabab88c86
SHA256_${LIBMAGIC_DIST} = 694c2432e5240187524c9e7cf1ec6acc77b47a0e19554d34c14773e43dbbf214
SHA256_${RUBY_DIST} = 254f1c1a79e4cc814d1e7320bc5bdd995dc57e08727d30a767664619a9c8ae5a
SHA256_${BZ2_DIST} = a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
-SHA256_${PYTHON_DIST} = a4f05a0720ce0fd92626f0278b6b433eee9a6173ddf2bced7957dfb599a5ece1
+SHA256_${PYTHON_DIST} = 8ecc681ea0600bbfb366f2b173f727b205bb825d93d2f0b286bc4e58d37693da
+SHA256_${PY3C_DIST} = c7ffc22bc92dded0ca859db53ef3a0b466f89a9f8aad29359c9fe4ff18ebdd20
SHA256_${JUNIT_DIST} = 36a747ca1e0b86f6ea88055b8723bb87030d627766da6288bf077afdeeb0f75a
SHA256_${GETTEXT_DIST} = ff942af0e438ced4a8b0ea4b0b6e0d6d657157c5e2364de57baa279c1c125c43
SHA256_${LZ4_DIST} = 0190cacd63022ccb86f44fa5041dc6c3804407ad61550ca21c382827319e7e7e
@@ -174,6 +177,7 @@ DISTFILES = $(DISTDIR)/$(NEON_DIST) \
$(DISTDIR)/$(RUBY_DIST) \
$(DISTDIR)/$(BZ2_DIST) \
$(DISTDIR)/$(PYTHON_DIST) \
+ $(DISTDIR)/$(PY3C_DIST) \
$(DISTDIR)/$(JUNIT_DIST) \
$(DISTDIR)/$(GETTEXT_DIST)
@@ -197,6 +201,7 @@ LIBMAGIC_URL = ftp://ftp.astron.com/pub/file/$(LIBMAGIC_DIST)
RUBY_URL = https://cache.ruby-lang.org/pub/ruby/2.4/$(RUBY_DIST)
BZ2_URL = https://stsp.name/distfiles/$(BZ2_DIST)
PYTHON_URL = https://python.org/ftp/python/$(PYTHON_VER)/$(PYTHON_DIST)
+PY3C_URL = https://stsp.name/distfiles/py3c-${PY3C_VER}.tar.gz
JUNIT_URL = https://stsp.name/distfiles/$(JUNIT_DIST)
GETTEXT_URL = https://ftp.gnu.org/pub/gnu/gettext/$(GETTEXT_DIST)
LZ4_URL = https://github.com/lz4/lz4/archive/v$(LZ4_VER).tar.gz
@@ -218,6 +223,7 @@ LIBMAGIC_SRCDIR = $(SRCDIR)/file-$(LIBMAGIC_VER)
RUBY_SRCDIR = $(SRCDIR)/ruby-$(RUBY_VER)
BZ2_SRCDIR = $(SRCDIR)/bzip2-$(BZ2_VER)
PYTHON_SRCDIR = $(SRCDIR)/Python-$(PYTHON_VER)
+PY3C_SRCDIR = $(SRCDIR)/py3c-$(PY3C_VER)
GETTEXT_SRCDIR = $(SRCDIR)/gettext-$(GETTEXT_VER)
LZ4_SRCDIR = ${SRCDIR}/lz4-$(LZ4_VER)
SVN_SRCDIR = $(SVN_WC)
@@ -238,6 +244,7 @@ LIBMAGIC_OBJDIR = $(OBJDIR)/file-$(LIBMAGIC_VER)
RUBY_OBJDIR = $(OBJDIR)/ruby-$(RUBY_VER)
BZ2_OBJDIR = $(OBJDIR)/bzip2-$(BZ2_VER)
PYTHON_OBJDIR = $(OBJDIR)/python-$(PYTHON_VER)
+PY3C_OBJDIR = $(OBJDIR)/python-$(PY3C_VER)
GETTEXT_OBJDIR = $(OBJDIR)/gettext-$(GETTEXT_VER)
LZ4_OBJDIR = ${OBJDIR}/lz4-$(LZ4_VER)
SVN_OBJDIR = $(OBJDIR)/$(SVN_REL_WC)
@@ -267,7 +274,7 @@ PKG_CONFIG_PATH=$(PREFIX)/apr/lib/pkgconfig:$(PREFIX)/neon/lib/pkgconfig:$(PREFI
all: dirs-create bdb-install apr-install iconv-install apr-util-install \
pcre-install httpd-install neon-install serf-install serf-old-install \
sqlite-install cyrus-sasl-install libmagic-install \
- ruby-install bz2-install python-install gettext-install \
+ ruby-install bz2-install python-install py3c-retrieve gettext-install \
lz4-install svn-install svn-bindings-install
# Use these to start a build from the beginning.
@@ -1199,6 +1206,56 @@ $(RUBY_OBJDIR)/openssl_missing.patch:
echo >> $@.tmp ' #if defined(HAVE_AUTHENTICATED_ENCRYPTION) && !defined(EVP_CTRL_AEAD_GET_TAG)'
mv -f $@.tmp $@
+$(RUBY_OBJDIR)/sparc64_buserror.patch:
+ mkdir -p $(dir $@)
+ echo > $@.tmp '--- compile.c.orig Thu Mar 12 12:58:26 2020'
+ echo >> $@.tmp '+++ compile.c Thu Mar 12 16:36:55 2020'
+ echo >> $@.tmp '@@ -751,6 +751,16 @@'
+ echo >> $@.tmp ' #define STRICT_ALIGNMENT'
+ echo >> $@.tmp ' #endif'
+ echo >> $@.tmp ' '
+ echo >> $@.tmp '+/*'
+ echo >> $@.tmp '+ * Some OpenBSD platforms (including sparc64) require strict alignment.'
+ echo >> $@.tmp '+ */'
+ echo >> $@.tmp '+#if defined(__OpenBSD__)'
+ echo >> $@.tmp '+ #include <sys/endian.h>'
+ echo >> $@.tmp '+ #ifdef __STRICT_ALIGNMENT'
+ echo >> $@.tmp '+ #define STRICT_ALIGNMENT'
+ echo >> $@.tmp '+ #endif'
+ echo >> $@.tmp '+#endif'
+ echo >> $@.tmp '+'
+ echo >> $@.tmp ' #ifdef STRICT_ALIGNMENT'
+ echo >> $@.tmp ' #if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE'
+ echo >> $@.tmp ' #define ALIGNMENT_SIZE SIZEOF_LONG_LONG'
+ echo >> $@.tmp '@@ -2094,7 +2104,8 @@'
+ echo >> $@.tmp ' unref_destination(iobj, 0);'
+ echo >> $@.tmp ' REMOVE_ELEM(&iobj->link);'
+ echo >> $@.tmp ' }'
+ echo >> $@.tmp '- else if (iobj != diobj && IS_INSN_ID(diobj, jump) &&'
+ echo >> $@.tmp '+ else if (iobj != diobj && IS_INSN(&diobj->link) &&'
+ echo >> $@.tmp '+ IS_INSN_ID(diobj, jump) &&'
+ echo >> $@.tmp ' OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0)) {'
+ echo >> $@.tmp ' replace_destination(iobj, diobj);'
+ echo >> $@.tmp ' remove_unreachable_chunk(iseq, iobj->link.next);'
+ echo >> $@.tmp '@@ -2136,6 +2147,7 @@'
+ echo >> $@.tmp ' * L2:'
+ echo >> $@.tmp ' */'
+ echo >> $@.tmp ' else if ((piobj = (INSN *)get_prev_insn(iobj)) != 0 &&'
+ echo >> $@.tmp '+ IS_INSN(&piobj->link) &&'
+ echo >> $@.tmp ' (IS_INSN_ID(piobj, branchif) ||'
+ echo >> $@.tmp ' IS_INSN_ID(piobj, branchunless))) {'
+ echo >> $@.tmp ' if (niobj == (INSN *)get_destination_insn(piobj)) {'
+ echo >> $@.tmp '@@ -2176,7 +2188,7 @@'
+ echo >> $@.tmp ' }'
+ echo >> $@.tmp ' '
+ echo >> $@.tmp ' for (;;) {'
+ echo >> $@.tmp '- if (IS_INSN_ID(nobj, jump)) {'
+ echo >> $@.tmp '+ if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {'
+ echo >> $@.tmp ' replace_destination(iobj, nobj);'
+ echo >> $@.tmp ' }'
+ echo >> $@.tmp ' else if (prev_dup && IS_INSN_ID(nobj, dup) &&'
+ mv -f $@.tmp $@
+
ifeq ($(UNAME),OpenBSD)
RUBY_SSL_EX_NEW_DATA_PATCH = sed -i -e '/^have_func("X509_STORE_set_ex_data")$$/ { p; s/^.*$$/\have_func("X509_STORE_get_ex_new_index")/; }'
else
@@ -1207,13 +1264,14 @@ endif
# retrieve ruby
#
-$(RUBY_OBJDIR)/.retrieved: $(DISTDIR)/$(RUBY_DIST) $(RUBY_OBJDIR)/openssl_missing.patch
+$(RUBY_OBJDIR)/.retrieved: $(DISTDIR)/$(RUBY_DIST) $(RUBY_OBJDIR)/openssl_missing.patch $(RUBY_OBJDIR)/sparc64_buserror.patch
$(call do_check_sha256,$(RUBY_DIST))
[ -d $(RUBY_OBJDIR) ] || mkdir -p $(RUBY_OBJDIR)
tar -C $(SRCDIR) -zxf $(DISTDIR)/$(RUBY_DIST)
-which ghead && sed -i -e "s/head -c/ghead -c/" $(RUBY_SRCDIR)/configure
$(RUBY_SSL_EX_NEW_DATA_PATCH) $(RUBY_SRCDIR)/ext/openssl/extconf.rb
cd $(RUBY_SRCDIR) && patch -p0 < $(RUBY_OBJDIR)/openssl_missing.patch
+ cd $(RUBY_SRCDIR) && patch -p0 < $(RUBY_OBJDIR)/sparc64_buserror.patch
touch $@
ifeq ($(THREADING),yes)
@@ -1304,14 +1362,9 @@ python-clean:
$(DISTDIR)/$(PYTHON_DIST):
cd $(DISTDIR) && $(FETCH_CMD) $(PYTHON_URL)
-# https://bugs.python.org/issue12560
-$(DISTDIR)/python-issue12560.patch:
- cd $(DISTDIR) && $(FETCH_CMD) -O "$@" \
- https://hg.python.org/cpython/raw-rev/32cc37a89b58
-
# retrieve python
#
-$(PYTHON_OBJDIR)/.retrieved: $(DISTDIR)/$(PYTHON_DIST) $(DISTDIR)/python-issue12560.patch
+$(PYTHON_OBJDIR)/.retrieved: $(DISTDIR)/$(PYTHON_DIST)
$(call do_check_sha256,$(PYTHON_DIST))
[ -d $(PYTHON_OBJDIR) ] || mkdir -p $(PYTHON_OBJDIR)
tar -C $(SRCDIR) -zxf $(DISTDIR)/$(PYTHON_DIST)
@@ -1325,7 +1378,6 @@ $(PYTHON_OBJDIR)/.retrieved: $(DISTDIR)/$(PYTHON_DIST) $(DISTDIR)/python-issue12
> $(PYTHON_SRCDIR)/setup.py.patched
mv $(PYTHON_SRCDIR)/setup.py.patched $(PYTHON_SRCDIR)/setup.py
chmod +x $(PYTHON_SRCDIR)/setup.py
- cd $(PYTHON_SRCDIR) && patch -p1 < $(DISTDIR)/python-issue12560.patch
touch $@
# configure python
@@ -1346,6 +1398,7 @@ $(PYTHON_OBJDIR)/.configured: $(PYTHON_OBJDIR)/.retrieved \
--enable-shared \
--with-system-expat \
--with-dbmliborder=bdb \
+ --without-pymalloc \
$(PYTHON_PROFILING)
touch $@
@@ -1365,6 +1418,25 @@ $(PYTHON_OBJDIR)/.installed: $(PYTHON_OBJDIR)/.compiled
make install)
touch $@
+#######################################################################
+# py3c
+#######################################################################
+
+py3c-retrieve: $(PY3C_OBJDIR)/.retrieved
+py3c-reset:
+ $(foreach f, .retrieved .configured .compiled .installed, \
+ rm -f $(PY3C_OBJDIR)/$(f);)
+
+# fetch distfile for py3c
+$(DISTDIR)/$(PY3C_DIST):
+ cd $(DISTDIR) && $(FETCH_CMD) $(PY3C_URL)
+
+# retrieve py3c
+$(PY3C_OBJDIR)/.retrieved: $(DISTDIR)/$(PY3C_DIST)
+ $(call do_check_sha256,$(PY3C_DIST))
+ [ -d $(PY3C_OBJDIR) ] || mkdir -p $(PY3C_OBJDIR)
+ tar -C $(SRCDIR) -zxf $(DISTDIR)/$(PY3C_DIST)
+ touch $@
#######################################################################
# junit
@@ -1625,6 +1697,7 @@ $(SVN_OBJDIR)/.configured: $(SVN_OBJDIR)/.retrieved $(DISTDIR)/$(JUNIT_DIST) \
CXXFLAGS="-I$(PREFIX)/gettext/include -DAPR_POOL_DEBUG" \
LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):$$LD_LIBRARY_PATH" \
PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) \
+ PYTHON=$(PREFIX)/python/bin/python3 \
GREP="`which grep`" \
PATH=$(PREFIX)/ruby/bin:$(PREFIX)/python/bin:$(PREFIX)/gettext/bin:$$PATH \
$(SVN_SRCDIR)/configure \
@@ -1641,6 +1714,7 @@ $(SVN_OBJDIR)/.configured: $(SVN_OBJDIR)/.retrieved $(DISTDIR)/$(JUNIT_DIST) \
--without-gnome-keyring \
--with-berkeley-db="$(BDB_FLAG)" \
--with-ruby-sitedir="$(SVN_PREFIX)/lib/ruby/site_ruby" \
+ --with-py3c="$(SRCDIR)/py3c-${PY3C_VER}" \
--disable-mod-activation \
$(JAVAHL_FLAG) \
$(LIBMAGIC_FLAG) \
@@ -2142,6 +2216,7 @@ define do_check
env LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(LIB_PTHREAD_HACK) \
env MAKEFLAGS= make check PARALLEL=$(PARALLEL) CLEANUP=$(CLEANUP) \
EXCLUSIVE_WC_LOCKS=$(EXCLUSIVE_WC_LOCKS) \
+ THREADED=$(THREADED) \
SVN_BIN_DIR=$(SVN_PREFIX)/bin \
MEMCACHED_SERVER=$(MEMCACHED_SERVER) $1 FS_TYPE=$$fs; \
for log in tests.log fails.log; do \
diff --git a/tools/dev/unix-build/README b/tools/dev/unix-build/README
index 8a3780c..949f389 100644
--- a/tools/dev/unix-build/README
+++ b/tools/dev/unix-build/README
@@ -1,7 +1,7 @@
Introduction
============
Makefile.svn aids Subversion developers on unix-like systems set up an
-SVN development environment without requiring root priviliges. It does
+SVN development environment without requiring root privileges. It does
this by fetching Subversion along with many of its dependencies from
the internet and building them using sane defaults suitable for
development (for example, it invokes --enable-maintainer-mode while
diff --git a/tools/dev/which-error.py b/tools/dev/which-error.py
index b8e3800..ba02bb9 100755
--- a/tools/dev/which-error.py
+++ b/tools/dev/which-error.py
@@ -23,7 +23,7 @@
# under the License.
# ====================================================================
#
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/dev/which-error.py $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/dev/which-error.py $
# $LastChangedDate: 2016-04-30 08:16:53 +0000 (Sat, 30 Apr 2016) $
# $LastChangedBy: stefan2 $
# $LastChangedRevision: 1741723 $
diff --git a/tools/dist/backport.pl b/tools/dist/backport.pl
index df3da22..32c62e5 100755
--- a/tools/dist/backport.pl
+++ b/tools/dist/backport.pl
@@ -9,11 +9,11 @@ use v5.10.0; # needed for $^V
# experimental and "subject to change" in v5.18 (see perl5180delta). Every
# use of it now triggers a warning.
#
-# As of Perl v5.26.1, the semantics of given/when provided by Perl are
+# As of Perl v5.30.0, the semantics of given/when provided by Perl are
# compatible with those expected by the script, so disable the warning for
# those Perls. But don't try to disable the the warning category on Perls
# that don't know that category, since that breaks compilation.
-no if (v5.17.0 le $^V and $^V le v5.26.1),
+no if (v5.17.0 le $^V and $^V le v5.30.0),
warnings => 'experimental::smartmatch';
# Licensed to the Apache Software Foundation (ASF) under one
@@ -157,7 +157,7 @@ N: Move to the next entry. Do not prompt for the current entry again, even
revisions added, justification changed) in the repository.
(This is a local action that will not affect other people or bots.)
: Move to the next entry. Prompt for the current entry again in the next
- run of backport.pl.
+ run of backport.pl.
(That's a space character, ASCII 0x20.)
?: Display this list.
EOF
@@ -231,7 +231,7 @@ Both batch modes also perform a basic sanity-check on entries that declare
backport branches (via the "Branch:" header): if a backport branch is used, but
at least one of the revisions enumerated in the entry title had neither been
merged from $TRUNK to the branch root, nor been committed
-directly to the backport branch, the hourly bot will turn red and
+directly to the backport branch, the hourly bot will turn red and
nightly bot will skip the entry and email its admins. (The nightly bot does
not email the list on failure, since it doesn't use buildbot.)
@@ -262,6 +262,9 @@ numbers); it will be ignored. For example,
$0 "Committed revision 42." "\$Some_justification"
will nominate r42.
+Revision numbers within the last thousand revisions may be specified using
+the last three digits only.
+
The justification can be an arbitrarily-long string; if it is wider than the
available width, this script will wrap it for you (and allow you to review
the result before committing).
@@ -1238,6 +1241,20 @@ sub nominate_main {
die "Unable to proceed." if warned_cannot_commit "Nominating failed";
+ # To save typing, require just the last three digits if they're unambiguous.
+ my $BASE_revision = `$SVN info --show-item=revision` + 0;
+ if ($BASE_revision > 1000) {
+ my $residue = $BASE_revision % 1000;
+ my $thousands = $BASE_revision - $residue;
+ @revnums = map {
+ $_ >= 1000
+ ? $_
+ : $thousands + $_ - 1000 * ($_ > $residue)
+ }
+ @revnums;
+ }
+
+ # Deduplicate and sort
@revnums = sort { $a <=> $b } keys %{{ map { $_ => 1 } @revnums }};
die "No revision numbers specified" unless @revnums;
diff --git a/tools/dist/backport/merger.py b/tools/dist/backport/merger.py
index 863750c..9a556c0 100644
--- a/tools/dist/backport/merger.py
+++ b/tools/dist/backport/merger.py
@@ -151,13 +151,13 @@ def log_message_file(logmsg):
logmsg_file.write(logmsg)
logmsg_file.flush()
yield logmsg_file.name
-
+
def merge(entry, expected_stderr=None, *, commit=False):
"""Merges ENTRY into the working copy at cwd.
Do not commit the result, unless COMMIT is true. When committing,
remove ENTRY from its STATUS file prior to committing.
-
+
EXPECTED_STDERR will be passed to run_svn() for the actual 'merge' command."""
assert isinstance(entry, backport.status.StatusEntry)
@@ -225,7 +225,7 @@ def merge(entry, expected_stderr=None, *, commit=False):
if entry.branch:
revnum = last_changed_revision('./STATUS')
-
+
if commit:
# Sleep to avoid out-of-order commit notifications
if not os.getenv("SVN_BACKPORT_DONT_SLEEP"): # enabled by the test suite
diff --git a/tools/dist/backport/status.py b/tools/dist/backport/status.py
index 7ec378d..e7a4edd 100644
--- a/tools/dist/backport/status.py
+++ b/tools/dist/backport/status.py
@@ -85,7 +85,7 @@ class Paragraph:
def __init__(self, kind, text, entry, containing_section):
"""Constructor.
-
+
KIND is one of the Kind.* enumerators.
TEXT is the physical text in the file, used by unparsing.
@@ -109,7 +109,7 @@ class Paragraph:
@classmethod
def is_header(cls, para_text):
"""PARA_TEXT is a single physical paragraph, as a bare multiline string.
-
+
If PARA_TEXT is a section header, return the header text; else, return
False."""
lines = para_text.split('\n', 2)
@@ -135,7 +135,7 @@ class Paragraph:
def approved(self):
"TRUE if this paragraph is in the approved section, false otherwise."
- assert self.kind
+ assert self.kind
# ### backport.pl used to check just .startswith() here.
return self.section() == "Approved changes"
@@ -335,7 +335,7 @@ class StatusEntry:
def __init__(self, para_text, status_file=None):
"""Parse an entry from PARA_TEXT, and add it to SELF. PARA_TEXT must
contain exactly one entry, as a single multiline string.
-
+
STATUS_FILE is the StatusFile object containing this entry, if any.
"""
self.branch = None
@@ -499,7 +499,7 @@ class StatusEntry:
assert self.valid()
suffix = "" if len(self.logsummary) == 1 else " [...]"
return self.logsummary[0] + suffix
-
+
# Private for is_vetoed()
_re_vetoed = re.compile(r'^\s*(-1:|-1\s*[()])', re.MULTILINE)
def is_vetoed(self):
diff --git a/tools/dist/backport_tests.py b/tools/dist/backport_tests.py
index 27df294..26de6f0 100644
--- a/tools/dist/backport_tests.py
+++ b/tools/dist/backport_tests.py
@@ -528,7 +528,7 @@ def backport_double_conflict(sbox):
# ...
# Warning summary
# ===============
- #
+ #
# r4 (default logsummary): subshell exited with code 256
# And backport.pl would exit with exit code 1.
diff --git a/tools/dist/checksums.py b/tools/dist/checksums.py
index 6ac7231..f00ed46 100755
--- a/tools/dist/checksums.py
+++ b/tools/dist/checksums.py
@@ -19,7 +19,7 @@
# under the License.
#
#
-# Check MD5 and SHA-1 and SHA-2 signatures of files, using
+# Check MD5 and SHA-1 and SHA-2 signatures of files, using
# md5sums, sha1sums, and/or sha512sums as manifests
# Replaces the 'md5sum', 'sha1sum', and 'sha512sums' commands
# on systems that do not have them, such as Mac OS X or Windows.
diff --git a/tools/dist/create-minor-release-branch.py b/tools/dist/create-minor-release-branch.py
deleted file mode 100755
index a68fa23..0000000
--- a/tools/dist/create-minor-release-branch.py
+++ /dev/null
@@ -1,328 +0,0 @@
-#!/usr/bin/env python
-# python: coding=utf-8
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-# About this script:
-# This script is intended to automate steps in creating a new Subversion
-# minor release.
-
-import os
-import re
-import sys
-import logging
-import subprocess
-import argparse # standard in Python 2.7
-
-from release import Version
-
-
-# Some constants
-repos = 'https://svn.apache.org/repos/asf/subversion'
-secure_repos = 'https://svn.apache.org/repos/asf/subversion'
-buildbot_repos = 'https://svn.apache.org/repos/infra/infrastructure/buildbot/aegis/buildmaster'
-
-# Parameters
-dry_run = False
-
-# Local working copies
-base_dir = None # set by main()
-
-def get_trunk_wc_path(path=None):
- trunk_wc_path = os.path.join(base_dir, 'svn-trunk')
- if path is None: return trunk_wc_path
- return os.path.join(trunk_wc_path, path)
-def get_branch_wc_path(ver, path=None):
- branch_wc_path = os.path.join(base_dir, ver.branch + '.x')
- if path is None: return branch_wc_path
- return os.path.join(branch_wc_path, path)
-def get_buildbot_wc_path(path=None):
- buildbot_wc_path = os.path.join(base_dir, 'svn-buildmaster')
- if path is None: return buildbot_wc_path
- return os.path.join(buildbot_wc_path, path)
-
-def get_trunk_url():
- return secure_repos + '/trunk'
-def get_branch_url(ver):
- return secure_repos + '/branches/' + ver.branch + '.x'
-def get_tag_url(ver):
- return secure_repos + '/tags/' + ver.base
-def get_buildbot_url():
- return buildbot_repos
-
-#----------------------------------------------------------------------
-# Utility functions
-
-def run(cmd, dry_run=False):
- print('+ ' + ' '.join(cmd))
- if not dry_run:
- stdout = subprocess.check_output(cmd)
- print(stdout)
- else:
- print(' ## dry-run; not executed')
-
-def run_svn(cmd, dry_run=False):
- run(['svn'] + cmd, dry_run)
-
-def svn_commit(cmd):
- run_svn(['commit'] + cmd, dry_run=dry_run)
-
-def svn_copy_branch(src, dst, message):
- args = ['copy', src, dst, '-m', message]
- run_svn(args, dry_run=dry_run)
-
-def svn_checkout(url, wc, *args):
- args = ['checkout', url, wc] + list(args)
- run_svn(args)
-
-#----------------------------------------------------------------------
-def edit_file(path, pattern, replacement):
- print("Editing '%s'" % (path,))
- print(" pattern='%s'" % (pattern,))
- print(" replace='%s'" % (replacement,))
- old_text = open(path, 'r').read()
- new_text = re.sub(pattern, replacement, old_text)
- assert new_text != old_text
- open(path, 'w').write(new_text)
-
-def edit_changes_file(path, newtext):
- """Insert NEWTEXT in the 'CHANGES' file found at PATH,
- just before the first line that starts with 'Version '.
- """
- print("Prepending to '%s'" % (path,))
- print(" text='%s'" % (newtext,))
- lines = open(path, 'r').readlines()
- for i, line in enumerate(lines):
- if line.startswith('Version '):
- with open(path, 'w') as newfile:
- newfile.writelines(lines[:i])
- newfile.write(newtext)
- newfile.writelines(lines[i:])
- break
-
-#----------------------------------------------------------------------
-def make_release_branch(ver, revnum):
- svn_copy_branch(get_trunk_url() + '@' + (str(revnum) if revnum else ''),
- get_branch_url(ver),
- 'Create the ' + ver.branch + '.x release branch.')
-
-#----------------------------------------------------------------------
-def update_minor_ver_in_trunk(ver, revnum):
- """Change the minor version in trunk to the next (future) minor version.
- """
- trunk_wc = get_trunk_wc_path()
- trunk_url = get_trunk_url()
- svn_checkout(trunk_url + '@' + (str(revnum) if revnum else ''),
- trunk_wc)
-
- prev_ver = Version('1.%d.0' % (ver.minor - 1,))
- next_ver = Version('1.%d.0' % (ver.minor + 1,))
- relpaths = []
-
- relpath = 'subversion/include/svn_version.h'
- relpaths.append(relpath)
- edit_file(get_trunk_wc_path(relpath),
- r'(#define SVN_VER_MINOR *)%s' % (ver.minor,),
- r'\g<1>%s' % (next_ver.minor,))
-
- relpath = 'subversion/tests/cmdline/svntest/main.py'
- relpaths.append(relpath)
- edit_file(get_trunk_wc_path(relpath),
- r'(SVN_VER_MINOR = )%s' % (ver.minor,),
- r'\g<1>%s' % (next_ver.minor,))
-
- relpath = 'subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java'
- relpaths.append(relpath)
- try:
- # since r1817921 (just after branching 1.10)
- edit_file(get_trunk_wc_path(relpath),
- r'SVN_VER_MINOR = %s;' % (ver.minor,),
- r'SVN_VER_MINOR = %s;' % (next_ver.minor,))
- except:
- # before r1817921: two separate places
- edit_file(get_trunk_wc_path(relpath),
- r'version.isAtLeast\(1, %s, 0\)' % (ver.minor,),
- r'version.isAtLeast\(1, %s, 0\)' % (next_ver.minor,))
- edit_file(get_trunk_wc_path(relpath),
- r'1.%s.0, but' % (ver.minor,),
- r'1.%s.0, but' % (next_ver.minor,))
-
- relpath = 'CHANGES'
- relpaths.append(relpath)
- # insert at beginning of CHANGES file
- edit_changes_file(get_trunk_wc_path(relpath),
- 'Version ' + next_ver.base + '\n'
- + '(?? ??? 20XX, from /branches/' + next_ver.branch + '.x)\n'
- + get_tag_url(next_ver) + '\n'
- + '\n')
-
- log_msg = '''\
-Increment the trunk version number to %s, and introduce a new CHANGES
-section, following the creation of the %s.x release branch.
-
-* subversion/include/svn_version.h,
- subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java,
- subversion/tests/cmdline/svntest/main.py
- (SVN_VER_MINOR): Increment to %s.
-
-* CHANGES: New section for %s.0.
-''' % (next_ver.branch, ver.branch, next_ver.minor, next_ver.branch)
- commit_paths = [get_trunk_wc_path(p) for p in relpaths]
- svn_commit(commit_paths + ['-m', log_msg])
-
-#----------------------------------------------------------------------
-def create_status_file_on_branch(ver):
- branch_wc = get_branch_wc_path(ver)
- branch_url = get_branch_url(ver)
- svn_checkout(branch_url, branch_wc, '--depth=immediates')
-
- status_local_path = os.path.join(branch_wc, 'STATUS')
- text='''\
- * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * *
- * THIS RELEASE STREAM IS OPEN FOR STABILIZATION. *
- * *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-This file tracks the status of releases in the %s.x line.
-
-See http://subversion.apache.org/docs/community-guide/releasing.html#release-stabilization
-for details on how release lines and voting work, what kinds of bugs can
-delay a release, etc.
-
-Status of %s:
-
-Candidate changes:
-==================
-
-
-Veto-blocked changes:
-=====================
-
-
-Approved changes:
-=================
-''' % (ver.branch, ver.base)
- open(status_local_path, 'wx').write(text)
- run_svn(['add', status_local_path])
- svn_commit([status_local_path,
- '-m', '* branches/' + ver.branch + '.x/STATUS: New file.'])
-
-#----------------------------------------------------------------------
-def update_backport_bot(ver):
- print("""MANUAL STEP: Fork & edit & pull-request on GitHub:
-https://github.com/apache/infrastructure-puppet/blob/deployment/modules/svnqavm_pvm_asf/manifests/init.pp
-"Add new %s.x branch to list of backport branches"
-""" % (ver.branch,))
- print("""Someone needs to run the 'svn checkout' manually.
-The exact checkout command is documented in machines/svn-qavm2/notes.txt
-in the private repository (need to use a trunk client and the svn-master.a.o
-hostname).
-""")
-
-#----------------------------------------------------------------------
-def update_buildbot_config(ver):
- """Add the new branch to the list of branches monitored by the buildbot
- master.
- """
- buildbot_wc = get_buildbot_wc_path()
- buildbot_url = get_buildbot_url()
- svn_checkout(buildbot_url, buildbot_wc)
-
- prev_ver = Version('1.%d.0' % (ver.minor - 1,))
- next_ver = Version('1.%d.0' % (ver.minor + 1,))
-
- relpath = 'master1/projects/subversion.conf'
- edit_file(get_buildbot_wc_path(relpath),
- r'(MINOR_LINES=\[.*%s)(\])' % (prev_ver.minor,),
- r'\1, %s\2' % (ver.minor,))
-
- log_msg = '''\
-Subversion: start monitoring the %s branch.
-''' % (ver.branch)
- commit_paths = [get_buildbot_wc_path(relpath)]
- svn_commit(commit_paths + ['-m', log_msg])
-
-#----------------------------------------------------------------------
-def create_release_branch(args):
- make_release_branch(args.version, args.revnum)
- update_minor_ver_in_trunk(args.version, args.revnum)
- create_status_file_on_branch(args.version)
- update_backport_bot(args.version)
- update_buildbot_config(args.version)
-
-
-#----------------------------------------------------------------------
-# Main entry point for argument parsing and handling
-
-def main():
- 'Parse arguments, and drive the appropriate subcommand.'
-
- # Setup our main parser
- parser = argparse.ArgumentParser(
- description='Create an Apache Subversion release branch.')
- subparsers = parser.add_subparsers(title='subcommands')
-
- # Setup the parser for the create-release-branch subcommand
- subparser = subparsers.add_parser('create-release-branch',
- help='''Create a minor release branch: branch from trunk,
- update version numbers on trunk, create status
- file on branch, update backport bot,
- update buildbot config.''')
- subparser.set_defaults(func=create_release_branch)
- subparser.add_argument('version', type=Version,
- help='''A version number to indicate the branch, such as
- '1.7.0' (the '.0' is required).''')
- subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
- nargs='?', default=None,
- help='''The trunk revision number to base the branch on.
- Default is HEAD.''')
- subparser.add_argument('--dry-run', action='store_true', default=False,
- help='Avoid committing any changes to repositories.')
- subparser.add_argument('--verbose', action='store_true', default=False,
- help='Increase output verbosity')
- subparser.add_argument('--base-dir', default=os.getcwd(),
- help='''The directory in which to create needed files and
- folders. The default is the current working
- directory.''')
-
- # Parse the arguments
- args = parser.parse_args()
-
- global base_dir, dry_run
- base_dir = args.base_dir
- dry_run = args.dry_run
-
- # Set up logging
- logger = logging.getLogger()
- if args.verbose:
- logger.setLevel(logging.DEBUG)
- else:
- logger.setLevel(logging.INFO)
-
- # Make timestamps in tarballs independent of local timezone
- os.environ['TZ'] = 'UTC'
-
- # finally, run the subcommand, and give it the parsed arguments
- args.func(args)
-
-
-if __name__ == '__main__':
- main()
diff --git a/tools/dist/release-lines.yaml b/tools/dist/release-lines.yaml
new file mode 100644
index 0000000..4b39aac
--- /dev/null
+++ b/tools/dist/release-lines.yaml
@@ -0,0 +1,30 @@
+# This file specifies metadata that may vary between release lines, that
+# is usually common to all releases in a release line, for all potentially
+# interesting release lines.
+---
+tool_versions:
+ '1.8':
+ autoconf: ['2.69', '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969']
+ libtool: ['2.4.3', '36b4881c1843d7585de9c66c4c3d9a067ed3a3f792bc670beba21f5a4960acdf']
+ swig: ['2.0.9', '586954000d297fafd7e91d1ad31089cc7e249f658889d11a44605d3662569539']
+ '1.9':
+ autoconf: ['2.69', '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969']
+ libtool: ['2.4.6', 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3']
+ swig: ['2.0.12', '65e13f22a60cecd7279c59882ff8ebe1ffe34078e85c602821a541817a4317f7']
+ '1.10': &TOOL_VERSIONS_1_10
+ autoconf: ['2.69', '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969']
+ libtool: ['2.4.6', 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3']
+ swig: ['3.0.12', '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d']
+ '1.11': *TOOL_VERSIONS_1_10
+ '1.12': *TOOL_VERSIONS_1_10
+ '1.13': *TOOL_VERSIONS_1_10
+ '1.14': *TOOL_VERSIONS_1_10
+ 'trunk': *TOOL_VERSIONS_1_10
+
+# The version that is our current recommended release
+recommended_release: '1.13'
+# For clean-dist, a whitelist of artifacts to keep, by version.
+supported_release_lines: ['1.9', '1.10', '1.13']
+# Long-Term Support (LTS) versions
+lts_release_lines: ['1.9', '1.10', '1.14']
+
diff --git a/tools/dist/release.py b/tools/dist/release.py
index 5b12c00..141f937 100755
--- a/tools/dist/release.py
+++ b/tools/dist/release.py
@@ -41,7 +41,10 @@ import sys
import glob
import fnmatch
import shutil
-import urllib2
+try:
+ from urllib.request import urlopen # Python 3
+except:
+ from urllib2 import urlopen # Python 2
import hashlib
import tarfile
import logging
@@ -52,6 +55,7 @@ import itertools
import subprocess
import argparse # standard in Python 2.7
import io
+import yaml
import backport.status
@@ -67,78 +71,33 @@ except ImportError:
sys.path.remove(ezt_path)
+def get_dist_metadata_file_path():
+ return os.path.join(os.path.abspath(sys.path[0]), 'release-lines.yaml')
+
+# Read the dist metadata (about release lines)
+with open(get_dist_metadata_file_path(), 'r') as stream:
+ dist_metadata = yaml.safe_load(stream)
+
# Our required / recommended release tool versions by release branch
-tool_versions = {
- 'trunk' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.6',
- 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
- 'swig' : ['3.0.12',
- '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
- },
- '1.13' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.6',
- 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
- 'swig' : ['3.0.12',
- '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
- },
- '1.12' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.6',
- 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
- 'swig' : ['3.0.12',
- '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
- },
- '1.11' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.6',
- 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
- 'swig' : ['3.0.12',
- '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
- },
- '1.10' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.6',
- 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
- 'swig' : ['3.0.12',
- '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
- },
- '1.9' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.6',
- 'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
- 'swig' : ['2.0.12',
- '65e13f22a60cecd7279c59882ff8ebe1ffe34078e85c602821a541817a4317f7'],
- },
- '1.8' : {
- 'autoconf' : ['2.69',
- '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
- 'libtool' : ['2.4.3',
- '36b4881c1843d7585de9c66c4c3d9a067ed3a3f792bc670beba21f5a4960acdf'],
- 'swig' : ['2.0.9',
- '586954000d297fafd7e91d1ad31089cc7e249f658889d11a44605d3662569539'],
- },
-}
+tool_versions = dist_metadata['tool_versions']
# The version that is our current recommended release
-# ### TODO: derive this from svn_version.h; see ../../build/getversion.py
-recommended_release = '1.12'
+recommended_release = dist_metadata['recommended_release']
# For clean-dist, a whitelist of artifacts to keep, by version.
-supported_release_lines = frozenset({"1.9", "1.10", "1.12", "1.13"})
+supported_release_lines = frozenset(dist_metadata['supported_release_lines'])
+# Long-Term Support (LTS) versions
+lts_release_lines = frozenset(dist_metadata['lts_release_lines'])
# Some constants
-svn_repos = 'https://svn.apache.org/repos/asf/subversion'
-dist_repos = 'https://dist.apache.org/repos/dist'
+svn_repos = os.getenv('SVN_RELEASE_SVN_REPOS',
+ 'https://svn.apache.org/repos/asf/subversion')
+dist_repos = os.getenv('SVN_RELEASE_DIST_REPOS',
+ 'https://dist.apache.org/repos/dist')
dist_dev_url = dist_repos + '/dev/subversion'
dist_release_url = dist_repos + '/release/subversion'
dist_archive_url = 'https://archive.apache.org/dist/subversion'
+buildbot_repos = os.getenv('SVN_RELEASE_BUILDBOT_REPOS',
+ 'https://svn.apache.org/repos/infra/infrastructure/buildbot/aegis/buildmaster')
KEYS = 'https://people.apache.org/keys/group/subversion.asc'
extns = ['zip', 'tar.gz', 'tar.bz2']
@@ -183,18 +142,6 @@ class Version(object):
def is_prerelease(self):
return self.pre != None
- def is_recommended(self):
- return self.branch == recommended_release
-
- def get_download_anchor(self):
- if self.is_prerelease():
- return 'pre-releases'
- else:
- if self.is_recommended():
- return 'recommended-release'
- else:
- return 'supported-releases'
-
def get_ver_tags(self, revnum):
# These get substituted into svn_version.h
ver_tag = ''
@@ -212,7 +159,7 @@ class Version(object):
ver_tag = '" (Nightly Build r%d)"' % revnum
ver_numtag = '"-nightly-r%d"' % revnum
else:
- ver_tag = '" (r%d)"' % revnum
+ ver_tag = '" (r%d)"' % revnum
ver_numtag = '""'
return (ver_tag, ver_numtag)
@@ -282,15 +229,12 @@ def get_exportdir(base_dir, version, revnum):
return os.path.join(get_tempdir(base_dir),
'subversion-%s-r%d' % (version, revnum))
-def get_deploydir(base_dir):
- return os.path.join(base_dir, 'deploy')
-
def get_target(args):
"Return the location of the artifacts"
if args.target:
return args.target
else:
- return get_deploydir(args.base_dir)
+ return os.path.join(args.base_dir, 'deploy')
def get_branch_path(args):
if not args.branch:
@@ -309,12 +253,14 @@ def get_tmplfile(filename):
return open(os.path.join(get_tmpldir(), filename))
except IOError:
# Hmm, we had a problem with the local version, let's try the repo
- return urllib2.urlopen(svn_repos + '/trunk/tools/dist/templates/' + filename)
+ return urlopen(svn_repos + '/trunk/tools/dist/templates/' + filename)
def get_nullfile():
return open(os.path.devnull, 'w')
-def run_script(verbose, script, hide_stderr=False):
+def run_command(cmd, verbose=True, hide_stderr=False, dry_run=False):
+ if verbose:
+ print("+ " + ' '.join(cmd))
stderr = None
if verbose:
stdout = None
@@ -323,23 +269,62 @@ def run_script(verbose, script, hide_stderr=False):
if hide_stderr:
stderr = get_nullfile()
+ if not dry_run:
+ subprocess.check_call(cmd, stdout=stdout, stderr=stderr)
+ else:
+ print(' ## dry-run; not executed')
+
+def run_script(verbose, script, hide_stderr=False):
for l in script.split('\n'):
- subprocess.check_call(l.split(), stdout=stdout, stderr=stderr)
+ run_command(l.split(), verbose, hide_stderr)
def download_file(url, target, checksum):
- response = urllib2.urlopen(url)
- target_file = open(target, 'w+')
+ """Download the file at URL to the local path TARGET.
+ If CHECKSUM is a string, verify the checksum of the downloaded
+ file and raise RuntimeError if it does not match. If CHECKSUM
+ is None, do not verify the downloaded file.
+ """
+ assert checksum is None or isinstance(checksum, str)
+
+ response = urlopen(url)
+ target_file = open(target, 'w+b')
target_file.write(response.read())
target_file.seek(0)
m = hashlib.sha256()
m.update(target_file.read())
target_file.close()
checksum2 = m.hexdigest()
- if checksum != checksum2:
+ if checksum is not None and checksum != checksum2:
raise RuntimeError("Checksum mismatch for '%s': "\
"downloaded: '%s'; expected: '%s'" % \
(target, checksum, checksum2))
+def run_svn(cmd, verbose=True, dry_run=False, username=None):
+ if (username):
+ cmd[:0] = ['--username', username]
+ run_command(['svn'] + cmd, verbose=verbose, dry_run=dry_run)
+
+def run_svnmucc(cmd, verbose=True, dry_run=False, username=None):
+ if (username):
+ cmd[:0] = ['--username', username]
+ run_command(['svnmucc'] + cmd, verbose=verbose, dry_run=dry_run)
+
+#----------------------------------------------------------------------
+def is_lts(version):
+ return version.branch in lts_release_lines
+
+def is_recommended(version):
+ return version.branch == recommended_release
+
+def get_download_anchor(version):
+ if version.is_prerelease():
+ return 'pre-releases'
+ else:
+ if is_recommended(version):
+ return 'recommended-release'
+ else:
+ return 'supported-releases'
+
#----------------------------------------------------------------------
# ezt helpers
@@ -363,7 +348,7 @@ def cleanup(args):
shutil.rmtree(get_prefix(args.base_dir), True)
shutil.rmtree(get_tempdir(args.base_dir), True)
- shutil.rmtree(get_deploydir(args.base_dir), True)
+ shutil.rmtree(get_target(args), True)
#----------------------------------------------------------------------
@@ -378,7 +363,8 @@ class RollDep(object):
def _test_version(self, cmd):
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ stderr=subprocess.STDOUT,
+ universal_newlines=True)
(stdout, stderr) = proc.communicate()
rc = proc.wait()
if rc: return ''
@@ -520,13 +506,236 @@ def build_env(args):
#----------------------------------------------------------------------
+# Create a new minor release branch
+
+def get_trunk_wc_path(base_dir, path=None):
+ trunk_wc_path = os.path.join(get_tempdir(base_dir), 'svn-trunk')
+ if path is None: return trunk_wc_path
+ return os.path.join(trunk_wc_path, path)
+
+def get_buildbot_wc_path(base_dir, path=None):
+ buildbot_wc_path = os.path.join(get_tempdir(base_dir), 'svn-buildmaster')
+ if path is None: return buildbot_wc_path
+ return os.path.join(buildbot_wc_path, path)
+
+def get_trunk_url(revnum=None):
+ return svn_repos + '/trunk' + '@' + (str(revnum) if revnum else '')
+
+def get_branch_url(ver):
+ return svn_repos + '/branches/' + ver.branch + '.x'
+
+def get_tag_url(ver):
+ return svn_repos + '/tags/' + ver.base
+
+def edit_file(path, pattern, replacement):
+ print("Editing '%s'" % (path,))
+ print(" pattern='%s'" % (pattern,))
+ print(" replace='%s'" % (replacement,))
+ old_text = open(path, 'r').read()
+ new_text = re.sub(pattern, replacement, old_text)
+ assert new_text != old_text
+ open(path, 'w').write(new_text)
+
+def edit_changes_file(path, newtext):
+ """Insert NEWTEXT in the 'CHANGES' file found at PATH,
+ just before the first line that starts with 'Version '.
+ """
+ print("Prepending to '%s'" % (path,))
+ print(" text='%s'" % (newtext,))
+ lines = open(path, 'r').readlines()
+ for i, line in enumerate(lines):
+ if line.startswith('Version '):
+ with open(path, 'w') as newfile:
+ newfile.writelines(lines[:i])
+ newfile.write(newtext)
+ newfile.writelines(lines[i:])
+ break
+
+#----------------------------------------------------------------------
+def make_release_branch(args):
+ ver = args.version
+ run_svn(['copy',
+ get_trunk_url(args.revnum),
+ get_branch_url(ver),
+ '-m', 'Create the ' + ver.branch + '.x release branch.'],
+ dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def update_minor_ver_in_trunk(args):
+ """Change the minor version in trunk to the next (future) minor version.
+ """
+ ver = args.version
+ trunk_wc = get_trunk_wc_path(args.base_dir)
+ run_svn(['checkout',
+ get_trunk_url(args.revnum),
+ trunk_wc])
+
+ prev_ver = Version('1.%d.0' % (ver.minor - 1,))
+ next_ver = Version('1.%d.0' % (ver.minor + 1,))
+ relpaths = []
+
+ relpath = 'subversion/include/svn_version.h'
+ relpaths.append(relpath)
+ edit_file(get_trunk_wc_path(args.base_dir, relpath),
+ r'(#define SVN_VER_MINOR *)%s' % (ver.minor,),
+ r'\g<1>%s' % (next_ver.minor,))
+
+ relpath = 'subversion/tests/cmdline/svntest/main.py'
+ relpaths.append(relpath)
+ edit_file(get_trunk_wc_path(args.base_dir, relpath),
+ r'(SVN_VER_MINOR = )%s' % (ver.minor,),
+ r'\g<1>%s' % (next_ver.minor,))
+
+ relpath = 'subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java'
+ relpaths.append(relpath)
+ try:
+ # since r1817921 (just after branching 1.10)
+ edit_file(get_trunk_wc_path(args.base_dir, relpath),
+ r'SVN_VER_MINOR = %s;' % (ver.minor,),
+ r'SVN_VER_MINOR = %s;' % (next_ver.minor,))
+ except:
+ # before r1817921: two separate places
+ edit_file(get_trunk_wc_path(args.base_dir, relpath),
+ r'version.isAtLeast\(1, %s, 0\)' % (ver.minor,),
+ r'version.isAtLeast\(1, %s, 0\)' % (next_ver.minor,))
+ edit_file(get_trunk_wc_path(args.base_dir, relpath),
+ r'1.%s.0, but' % (ver.minor,),
+ r'1.%s.0, but' % (next_ver.minor,))
+
+ relpath = 'CHANGES'
+ relpaths.append(relpath)
+ # insert at beginning of CHANGES file
+ edit_changes_file(get_trunk_wc_path(args.base_dir, relpath),
+ 'Version ' + next_ver.base + '\n'
+ + '(?? ??? 20XX, from /branches/' + next_ver.branch + '.x)\n'
+ + get_tag_url(next_ver) + '\n'
+ + '\n')
+
+ log_msg = '''\
+Increment the trunk version number to %s, and introduce a new CHANGES
+section, following the creation of the %s.x release branch.
+
+* subversion/include/svn_version.h,
+ subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java,
+ subversion/tests/cmdline/svntest/main.py
+ (SVN_VER_MINOR): Increment to %s.
+
+* CHANGES: New section for %s.0.
+''' % (next_ver.branch, ver.branch, next_ver.minor, next_ver.branch)
+ commit_paths = [get_trunk_wc_path(args.base_dir, p) for p in relpaths]
+ run_svn(['commit'] + commit_paths + ['-m', log_msg],
+ dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def create_status_file_on_branch(args):
+ ver = args.version
+ branch_wc = get_workdir(args.base_dir)
+ branch_url = get_branch_url(ver)
+ run_svn(['checkout', branch_url, branch_wc, '--depth=immediates'])
+
+ status_local_path = os.path.join(branch_wc, 'STATUS')
+ template_filename = 'STATUS.ezt'
+ data = { 'major-minor' : ver.branch,
+ 'major-minor-patch' : ver.base,
+ }
+
+ template = ezt.Template(compress_whitespace=False)
+ template.parse(get_tmplfile(template_filename).read())
+
+ with open(status_local_path, 'wx') as g:
+ template.generate(g, data)
+ run_svn(['add', status_local_path])
+ run_svn(['commit', status_local_path,
+ '-m', '* branches/' + ver.branch + '.x/STATUS: New file.'],
+ dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def update_backport_bot(args):
+ ver = args.version
+ print("""\
+
+*** MANUAL STEP REQUIRED ***
+
+ Ask someone with appropriate access to add the %s.x branch
+ to the backport merge bot. See
+ http://subversion.apache.org/docs/community-guide/releasing.html#backport-merge-bot
+
+***
+
+""" % (ver.branch,))
+
+#----------------------------------------------------------------------
+def update_buildbot_config(args):
+ """Add the new branch to the list of branches monitored by the buildbot
+ master.
+ """
+ ver = args.version
+ buildbot_wc = get_buildbot_wc_path(args.base_dir)
+ run_svn(['checkout', buildbot_repos, buildbot_wc])
+
+ prev_ver = Version('1.%d.0' % (ver.minor - 1,))
+ next_ver = Version('1.%d.0' % (ver.minor + 1,))
+
+ relpath = 'master1/projects/subversion.conf'
+ edit_file(get_buildbot_wc_path(args.base_dir, relpath),
+ r'(MINOR_LINES=\[.*%s)(\])' % (prev_ver.minor,),
+ r'\1, %s\2' % (ver.minor,))
+
+ log_msg = '''\
+Subversion: start monitoring the %s branch.
+''' % (ver.branch)
+ commit_paths = [get_buildbot_wc_path(args.base_dir, relpath)]
+ run_svn(['commit'] + commit_paths + ['-m', log_msg],
+ dry_run=args.dry_run)
+
+#----------------------------------------------------------------------
+def create_release_branch(args):
+ make_release_branch(args)
+ update_minor_ver_in_trunk(args)
+ create_status_file_on_branch(args)
+ update_backport_bot(args)
+ update_buildbot_config(args)
+
+
+#----------------------------------------------------------------------
+def write_release_notes(args):
+
+ # Create a skeleton release notes file from template
+
+ template_filename = \
+ 'release-notes-lts.ezt' if is_lts(args.version) else 'release-notes.ezt'
+
+ prev_ver = Version('%d.%d.0' % (args.version.major, args.version.minor - 1))
+ data = { 'major-minor' : args.version.branch,
+ 'previous-major-minor' : prev_ver.branch,
+ }
+
+ template = ezt.Template(compress_whitespace=False)
+ template.parse(get_tmplfile(template_filename).read())
+
+ if args.edit_html_file:
+ with open(args.edit_html_file, 'w') as g:
+ template.generate(g, data)
+ else:
+ template.generate(sys.stdout, data)
+
+ # Add an "in progress" entry in the release notes index
+ #
+ index_file = os.path.normpath(args.edit_html_file + '/../index.html')
+ marker = '<ul id="release-notes-list">\n'
+ new_item = '<li><a href="%s.html">Subversion %s</a> – <i>in progress</i></li>\n' % (args.version.branch, args.version.branch)
+ edit_file(index_file,
+ re.escape(marker),
+ (marker + new_item).replace('\\', r'\\'))
+
+#----------------------------------------------------------------------
# Create release artifacts
def compare_changes(repos, branch, revision):
mergeinfo_cmd = ['svn', 'mergeinfo', '--show-revs=eligible',
repos + '/trunk/CHANGES',
repos + '/' + branch + '/' + 'CHANGES']
- stdout = subprocess.check_output(mergeinfo_cmd)
+ stdout = subprocess.check_output(mergeinfo_cmd, universal_newlines=True)
if stdout:
# Treat this as a warning since we are now putting entries for future
# minor releases in CHANGES on trunk.
@@ -544,7 +753,7 @@ def check_copyright_year(repos, branch, revision):
file_url = (repos + '/' + branch + '/'
+ branch_relpath + '@' + str(revision))
cat_cmd = ['svn', 'cat', file_url]
- stdout = subprocess.check_output(cat_cmd)
+ stdout = subprocess.check_output(cat_cmd, universal_newlines=True)
m = _copyright_re.search(stdout)
if m:
year = m.group('year')
@@ -596,17 +805,18 @@ def roll_tarballs(args):
compare_changes(svn_repos, branch, args.revnum)
# Ensure the output directory doesn't already exist
- if os.path.exists(get_deploydir(args.base_dir)):
+ if os.path.exists(get_target(args)):
raise RuntimeError('output directory \'%s\' already exists'
- % get_deploydir(args.base_dir))
+ % get_target(args))
- os.mkdir(get_deploydir(args.base_dir))
+ os.mkdir(get_target(args))
logging.info('Preparing working copy source')
shutil.rmtree(get_workdir(args.base_dir), True)
- run_script(args.verbose, 'svn checkout %s %s'
- % (svn_repos + '/' + branch + '@' + str(args.revnum),
- get_workdir(args.base_dir)))
+ run_svn(['checkout',
+ svn_repos + '/' + branch + '@' + str(args.revnum),
+ get_workdir(args.base_dir)],
+ verbose=args.verbose)
# Exclude stuff we don't want in the tarball, it will not be present
# in the exported tree.
@@ -617,8 +827,8 @@ def roll_tarballs(args):
exclude += ['packages', 'www']
cwd = os.getcwd()
os.chdir(get_workdir(args.base_dir))
- run_script(args.verbose,
- 'svn update --set-depth exclude %s' % " ".join(exclude))
+ run_svn(['update', '--set-depth=exclude'] + exclude,
+ verbose=args.verbose)
os.chdir(cwd)
if args.patches:
@@ -628,10 +838,10 @@ def roll_tarballs(args):
for name in os.listdir(args.patches):
if name.find(majmin) != -1 and name.endswith('patch'):
logging.info('Applying patch %s' % name)
- run_script(args.verbose,
- '''svn patch %s %s'''
- % (os.path.join(args.patches, name),
- get_workdir(args.base_dir)))
+ run_svn(['patch',
+ os.path.join(args.patches, name),
+ get_workdir(args.base_dir)],
+ verbose=args.verbose)
# Massage the new version number into svn_version.h.
ver_tag, ver_numtag = args.version.get_ver_tags(args.revnum)
@@ -666,11 +876,12 @@ def roll_tarballs(args):
def export(windows):
shutil.rmtree(exportdir, True)
if windows:
- eol_style = "--native-eol CRLF"
+ eol_style = "--native-eol=CRLF"
else:
- eol_style = "--native-eol LF"
- run_script(args.verbose, "svn export %s %s %s"
- % (eol_style, get_workdir(args.base_dir), exportdir))
+ eol_style = "--native-eol=LF"
+ run_svn(['export',
+ eol_style, get_workdir(args.base_dir), exportdir],
+ verbose=args.verbose)
def transform_sql():
for root, dirs, files in os.walk(exportdir):
@@ -744,25 +955,33 @@ def roll_tarballs(args):
for e in extns:
filename = basename + '.' + e
filepath = os.path.join(get_tempdir(args.base_dir), filename)
- shutil.move(filepath, get_deploydir(args.base_dir))
- filepath = os.path.join(get_deploydir(args.base_dir), filename)
+ shutil.move(filepath, get_target(args))
+ filepath = os.path.join(get_target(args), filename)
if args.version < Version("1.11.0-alpha1"):
# 1.10 and earlier generate *.sha1 files for compatibility reasons.
# They are deprecated, however, so we don't publicly link them in
# the announcements any more.
m = hashlib.sha1()
- m.update(open(filepath, 'r').read())
+ m.update(open(filepath, 'rb').read())
open(filepath + '.sha1', 'w').write(m.hexdigest())
m = hashlib.sha512()
- m.update(open(filepath, 'r').read())
+ m.update(open(filepath, 'rb').read())
open(filepath + '.sha512', 'w').write(m.hexdigest())
# Nightlies do not get tagged so do not need the header
if args.version.pre != 'nightly':
shutil.copy(os.path.join(get_workdir(args.base_dir),
'subversion', 'include', 'svn_version.h'),
- os.path.join(get_deploydir(args.base_dir),
- 'svn_version.h.dist-%s' % str(args.version)))
+ os.path.join(get_target(args),
+ 'svn_version.h.dist-%s'
+ % (str(args.version),)))
+
+ # Download and "tag" the KEYS file (in case a signing key is removed
+ # from a committer's LDAP profile down the road)
+ basename = 'subversion-%s.KEYS' % (str(args.version),)
+ filepath = os.path.join(get_tempdir(args.base_dir), basename)
+ download_file(KEYS, filepath, None)
+ shutil.move(filepath, get_target(args))
# And we're done!
@@ -804,14 +1023,12 @@ def post_candidates(args):
logging.info('Importing tarballs to %s' % dist_dev_url)
ver = str(args.version)
- svn_cmd = ['svn', 'import', '-m',
+ svn_cmd = ['import', '-m',
'Add Subversion %s candidate release artifacts' % ver,
'--auto-props', '--config-option',
'config:auto-props:*.asc=svn:eol-style=native;svn:mime-type=text/plain',
target, dist_dev_url]
- if (args.username):
- svn_cmd += ['--username', args.username]
- subprocess.check_call(svn_cmd)
+ run_svn(svn_cmd, verbose=args.verbose, username=args.username)
#----------------------------------------------------------------------
# Create tag
@@ -828,10 +1045,7 @@ def create_tag_only(args):
tag = svn_repos + '/tags/' + str(args.version)
- svnmucc_cmd = ['svnmucc', '-m',
- 'Tagging release ' + str(args.version)]
- if (args.username):
- svnmucc_cmd += ['--username', args.username]
+ svnmucc_cmd = ['-m', 'Tagging release ' + str(args.version)]
svnmucc_cmd += ['cp', str(args.revnum), branch_url, tag]
svnmucc_cmd += ['put', os.path.join(target, 'svn_version.h.dist' + '-' +
str(args.version)),
@@ -839,7 +1053,7 @@ def create_tag_only(args):
# don't redirect stdout/stderr since svnmucc might ask for a password
try:
- subprocess.check_call(svnmucc_cmd)
+ run_svnmucc(svnmucc_cmd, verbose=args.verbose, username=args.username)
except subprocess.CalledProcessError:
if args.version.is_prerelease():
logging.error("Do you need to pass --branch=trunk?")
@@ -878,7 +1092,8 @@ def bump_versions_on_branch(args):
args.version.patch + 1))
HEAD = subprocess.check_output(['svn', 'info', '--show-item=revision',
- '--', branch_url]).strip()
+ '--', branch_url],
+ universal_newlines=True).strip()
HEAD = int(HEAD)
def file_object_for(relpath):
fd = tempfile.NamedTemporaryFile()
@@ -898,13 +1113,14 @@ def bump_versions_on_branch(args):
svn_version_h.seek(0, os.SEEK_SET)
STATUS.seek(0, os.SEEK_SET)
- subprocess.check_call(['svnmucc', '-r', str(HEAD),
- '-m', 'Post-release housekeeping: '
- 'bump the %s branch to %s.'
- % (branch_url.split('/')[-1], str(new_version)),
- 'put', svn_version_h.name, svn_version_h.url,
- 'put', STATUS.name, STATUS.url,
- ])
+ run_svnmucc(['-r', str(HEAD),
+ '-m', 'Post-release housekeeping: '
+ 'bump the %s branch to %s.'
+ % (branch_url.split('/')[-1], str(new_version)),
+ 'put', svn_version_h.name, svn_version_h.url,
+ 'put', STATUS.name, STATUS.url,
+ ],
+ verbose=args.verbose, username=args.username)
del svn_version_h
del STATUS
@@ -924,7 +1140,8 @@ def clean_dist(args):
'''Clean the distribution directory of release artifacts of
no-longer-supported minor lines.'''
- stdout = subprocess.check_output(['svn', 'list', dist_release_url])
+ stdout = subprocess.check_output(['svn', 'list', dist_release_url],
+ universal_newlines=True)
def minor(version):
"""Return the minor release line of the parameter, which must be
@@ -946,10 +1163,8 @@ def clean_dist(args):
for i in sorted(to_keep):
logging.info("Saving release '%s'", i)
- svnmucc_cmd = ['svnmucc', '-m', 'Remove old Subversion releases.\n' +
+ svnmucc_cmd = ['-m', 'Remove old Subversion releases.\n' +
'They are still available at ' + dist_archive_url]
- if (args.username):
- svnmucc_cmd += ['--username', args.username]
for filename in filenames:
if Version(filename) not in to_keep:
logging.info("Removing %r", filename)
@@ -957,7 +1172,7 @@ def clean_dist(args):
# don't redirect stdout/stderr since svnmucc might ask for a password
if 'rm' in svnmucc_cmd:
- subprocess.check_call(svnmucc_cmd)
+ run_svnmucc(svnmucc_cmd, verbose=args.verbose, username=args.username)
else:
logging.info("Nothing to remove")
@@ -967,16 +1182,15 @@ def clean_dist(args):
def move_to_dist(args):
'Move candidate artifacts to the distribution directory.'
- stdout = subprocess.check_output(['svn', 'list', dist_dev_url])
+ stdout = subprocess.check_output(['svn', 'list', dist_dev_url],
+ universal_newlines=True)
filenames = []
for entry in stdout.split('\n'):
if fnmatch.fnmatch(entry, 'subversion-%s.*' % str(args.version)):
filenames.append(entry)
- svnmucc_cmd = ['svnmucc', '-m',
+ svnmucc_cmd = ['-m',
'Publish Subversion-%s.' % str(args.version)]
- if (args.username):
- svnmucc_cmd += ['--username', args.username]
svnmucc_cmd += ['rm', dist_dev_url + '/' + 'svn_version.h.dist'
+ '-' + str(args.version)]
for filename in filenames:
@@ -985,20 +1199,24 @@ def move_to_dist(args):
# don't redirect stdout/stderr since svnmucc might ask for a password
logging.info('Moving release artifacts to %s' % dist_release_url)
- subprocess.check_call(svnmucc_cmd)
+ run_svnmucc(svnmucc_cmd, verbose=args.verbose, username=args.username)
#----------------------------------------------------------------------
# Write announcements
def write_news(args):
'Write text for the Subversion website.'
- data = { 'date' : datetime.date.today().strftime('%Y%m%d'),
- 'date_pres' : datetime.date.today().strftime('%Y-%m-%d'),
+ if args.news_release_date:
+ release_date = datetime.datetime.strptime(args.news_release_date, '%Y-%m-%d')
+ else:
+ release_date = datetime.date.today()
+ data = { 'date' : release_date.strftime('%Y%m%d'),
+ 'date_pres' : release_date.strftime('%Y-%m-%d'),
'major-minor' : args.version.branch,
'version' : str(args.version),
'version_base' : args.version.base,
- 'anchor': args.version.get_download_anchor(),
- 'is_recommended': ezt_bool(args.version.is_recommended()),
+ 'anchor': get_download_anchor(args.version),
+ 'is_recommended': ezt_bool(is_recommended(args.version)),
'announcement_url': args.announcement_url,
}
@@ -1058,7 +1276,7 @@ def write_announcement(args):
'siginfo' : "\n".join(siginfo) + "\n",
'major-minor' : args.version.branch,
'major-minor-patch' : args.version.base,
- 'anchor' : args.version.get_download_anchor(),
+ 'anchor' : get_download_anchor(args.version),
}
if args.version.is_prerelease():
@@ -1153,9 +1371,9 @@ def get_siginfo(args, quiet=False):
% (n, filename, key_end))
sys.exit(1)
- fd, fn = tempfile.mkstemp()
- os.write(fd, key_start + key)
- os.close(fd)
+ fd, fn = tempfile.mkstemp(text=True)
+ with os.fdopen(fd, 'w') as key_file:
+ key_file.write(key_start + key)
verified = gpg.verify_file(open(fn, 'rb'), filename[:-4])
os.unlink(fn)
@@ -1177,6 +1395,7 @@ def get_siginfo(args, quiet=False):
gpg_output = subprocess.check_output(
['gpg', '--fixed-list-mode', '--with-colons', '--fingerprint', id],
stderr=subprocess.STDOUT,
+ universal_newlines=True,
)
gpg_output = gpg_output.splitlines()
@@ -1244,7 +1463,7 @@ def get_keys(args):
'Import the LDAP-based KEYS file to gpg'
# We use a tempfile because urlopen() objects don't have a .fileno()
with tempfile.SpooledTemporaryFile() as fd:
- fd.write(urllib2.urlopen(KEYS).read())
+ fd.write(urlopen(KEYS).read())
fd.flush()
fd.seek(0)
subprocess.check_call(['gpg', '--import'], stdin=fd)
@@ -1256,18 +1475,18 @@ def add_to_changes_dict(changes_dict, audience, section, change, revision):
if section:
section = section.lower()
change = change.strip()
-
+
if not audience in changes_dict:
changes_dict[audience] = dict()
if not section in changes_dict[audience]:
changes_dict[audience][section] = dict()
-
+
changes = changes_dict[audience][section]
if change in changes:
changes[change].add(revision)
else:
changes[change] = set([revision])
-
+
def print_section(changes_dict, audience, section, title, mandatory=False):
if audience in changes_dict:
audience_changes = changes_dict[audience]
@@ -1304,7 +1523,7 @@ def write_changelog(args):
# Putting [skip], [ignore], [c:skip] or [c:ignore] somewhere in the
# log message means this commit must be ignored for Changelog processing
# (ignored even with the --include-unlabeled-summaries option).
- #
+ #
# If there is no changes label anywhere in the commit message, and the
# --include-unlabeled-summaries option is used, we'll consider the summary
# line of the commit message (= first line except if it starts with a *)
@@ -1323,9 +1542,10 @@ def write_changelog(args):
previous = svn_repos + '/' + args.previous
include_unlabeled = args.include_unlabeled
separator_line = ('-' * 72) + '\n'
-
+
mergeinfo = subprocess.check_output(['svn', 'mergeinfo', '--show-revs',
- 'eligible', '--log', branch_url, previous])
+ 'eligible', '--log', branch_url, previous],
+ universal_newlines=True)
log_messages_dict = {
# This is a dictionary mapping revision numbers to their respective
# log messages. The expression in the "key:" part of the dict
@@ -1336,7 +1556,7 @@ def write_changelog(args):
for log_message in mergeinfo.split(separator_line)[1:-1]
}
mergeinfo = mergeinfo.splitlines()
-
+
separator_pattern = re.compile('^-{72}$')
revline_pattern = re.compile('^r(\d+) \| [^\|]+ \| [^\|]+ \| \d+ lines?$')
changes_prefix_pattern = re.compile(r'^\[(U|D)?:?([^\]]+)?\](.+)$')
@@ -1353,7 +1573,7 @@ def write_changelog(args):
audience = None
section = None
message = None
-
+
for line in mergeinfo:
if separator_pattern.match(line):
# New revision section. Reset variables.
@@ -1370,7 +1590,7 @@ def write_changelog(args):
# logic, in order to extract CHANGES_PREFIX_PATTERN
# and CHANGES_SUFFIX_PATTERN lines from the trunk log
# message.
-
+
# 2. Parse the STATUS entry
this_log_message = log_messages_dict[revision]
status_paragraph = this_log_message.split('\n\n')[2]
@@ -1411,7 +1631,7 @@ def write_changelog(args):
if re.search(r'\[(c:)?(skip|ignore)\]', line, re.IGNORECASE):
changes_ignore = True
-
+
prefix_match = changes_prefix_pattern.match(line)
if prefix_match:
audience = prefix_match.group(1)
@@ -1459,17 +1679,25 @@ def main():
parser = argparse.ArgumentParser(
description='Create an Apache Subversion release.')
parser.add_argument('--clean', action='store_true', default=False,
- help='Remove any directories previously created by %(prog)s')
+ help='''Remove any directories previously created by %(prog)s,
+ including the 'prefix' dir, the 'temp' dir, and the
+ default or specified target dir.''')
parser.add_argument('--verbose', action='store_true', default=False,
help='Increase output verbosity')
parser.add_argument('--base-dir', default=os.getcwd(),
help='''The directory in which to create needed files and
folders. The default is the current working
directory.''')
+ parser.add_argument('--target',
+ help='''The full path to the directory containing
+ release artifacts. Default: <BASE_DIR>/deploy''')
parser.add_argument('--branch',
help='''The branch to base the release on,
as a path relative to ^/subversion/.
Default: 'branches/MAJOR.MINOR.x'.''')
+ parser.add_argument('--username',
+ help='Username for committing to ' + svn_repos +
+ ' or ' + dist_repos + '.')
subparsers = parser.add_subparsers(title='subcommands')
# Setup the parser for the build-env subcommand
@@ -1487,6 +1715,40 @@ def main():
help='''Attempt to use existing build dependencies before
downloading and building a private set.''')
+ # Setup the parser for the create-release-branch subcommand
+ subparser = subparsers.add_parser('create-release-branch',
+ help='''Create a minor release branch: branch from trunk,
+ update version numbers on trunk, create status
+ file on branch, update backport bot,
+ update buildbot config.''')
+ subparser.set_defaults(func=create_release_branch)
+ subparser.add_argument('version', type=Version,
+ help='''A version number to indicate the branch, such as
+ '1.7.0' (the '.0' is required).''')
+ subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
+ nargs='?', default=None,
+ help='''The trunk revision number to base the branch on.
+ Default is HEAD.''')
+ subparser.add_argument('--dry-run', action='store_true', default=False,
+ help='Avoid committing any changes to repositories.')
+
+ # Setup the parser for the create-release-branch subcommand
+ subparser = subparsers.add_parser('write-release-notes',
+ help='''Write a template release-notes file.''')
+ subparser.set_defaults(func=write_release_notes)
+ subparser.add_argument('version', type=Version,
+ help='''A version number to indicate the branch, such as
+ '1.7.0' (the '.0' is required).''')
+ subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
+ nargs='?', default=None,
+ help='''The trunk revision number to base the branch on.
+ Default is HEAD.''')
+ subparser.add_argument('--edit-html-file',
+ help='''Write the template release-notes to this file,
+ and update 'index.html' in the same directory.''')
+ subparser.add_argument('--dry-run', action='store_true', default=False,
+ help='Avoid committing any changes to repositories.')
+
# Setup the parser for the roll subcommand
subparser = subparsers.add_parser('roll',
help='''Create the release artifacts.''')
@@ -1504,9 +1766,6 @@ def main():
subparser.set_defaults(func=sign_candidates)
subparser.add_argument('version', type=Version,
help='''The release label, such as '1.7.0-alpha1'.''')
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
subparser.add_argument('--userid',
help='''The (optional) USER-ID specifying the key to be
used for signing, such as '110B1C95' (Key-ID). If
@@ -1519,11 +1778,6 @@ def main():
subparser.set_defaults(func=post_candidates)
subparser.add_argument('version', type=Version,
help='''The release label, such as '1.7.0-alpha1'.''')
- subparser.add_argument('--username',
- help='''Username for ''' + dist_repos + '''.''')
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
# Setup the parser for the create-tag subcommand
subparser = subparsers.add_parser('create-tag',
@@ -1534,11 +1788,6 @@ def main():
help='''The release label, such as '1.7.0-alpha1'.''')
subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
help='''The revision number to base the release on.''')
- subparser.add_argument('--username',
- help='''Username for ''' + svn_repos + '''.''')
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
# Setup the parser for the bump-versions-on-branch subcommand
subparser = subparsers.add_parser('bump-versions-on-branch',
@@ -1548,11 +1797,6 @@ def main():
help='''The release label, such as '1.7.0-alpha1'.''')
subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
help='''The revision number to base the release on.''')
- subparser.add_argument('--username',
- help='''Username for ''' + svn_repos + '''.''')
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
# The clean-dist subcommand
subparser = subparsers.add_parser('clean-dist',
@@ -1560,8 +1804,6 @@ def main():
subparser.set_defaults(func=clean_dist)
subparser.add_argument('--dist-dir',
help='''The directory to clean.''')
- subparser.add_argument('--username',
- help='''Username for ''' + dist_repos + '''.''')
# The move-to-dist subcommand
subparser = subparsers.add_parser('move-to-dist',
@@ -1571,8 +1813,6 @@ def main():
subparser.set_defaults(func=move_to_dist)
subparser.add_argument('version', type=Version,
help='''The release label, such as '1.7.0-alpha1'.''')
- subparser.add_argument('--username',
- help='''Username for ''' + dist_repos + '''.''')
# The write-news subcommand
subparser = subparsers.add_parser('write-news',
@@ -1581,6 +1821,9 @@ def main():
subparser.set_defaults(func=write_news)
subparser.add_argument('--announcement-url',
help='''The URL to the archived announcement email.''')
+ subparser.add_argument('--news-release-date',
+ help='''The release date for the news, as YYYY-MM-DD.
+ Default: today.''')
subparser.add_argument('--edit-html-file',
help='''Insert the text into this file
news.html, index.html).''')
@@ -1595,9 +1838,6 @@ def main():
subparser.add_argument('--security', action='store_true', default=False,
help='''The release being announced includes security
fixes.''')
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
subparser.add_argument('version', type=Version,
help='''The release label, such as '1.7.0-alpha1'.''')
@@ -1606,9 +1846,6 @@ def main():
help='''Output to stdout template text for the download
table for subversion.apache.org''')
subparser.set_defaults(func=write_downloads)
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
subparser.add_argument('version', type=Version,
help='''The release label, such as '1.7.0-alpha1'.''')
@@ -1619,9 +1856,6 @@ def main():
subparser.set_defaults(func=check_sigs)
subparser.add_argument('version', type=Version,
help='''The release label, such as '1.7.0-alpha1'.''')
- subparser.add_argument('--target',
- help='''The full path to the directory containing
- release artifacts.''')
# get-keys
subparser = subparsers.add_parser('get-keys',
@@ -1641,7 +1875,7 @@ def main():
like [U:client], [D:api], [U], ...''')
subparser.set_defaults(func=write_changelog)
subparser.add_argument('previous',
- help='''The "previous" branch or tag, relative to
+ help='''The "previous" branch or tag, relative to
^/subversion/, to compare "branch" against.''')
subparser.add_argument('--include-unlabeled-summaries',
dest='include_unlabeled',
@@ -1652,7 +1886,7 @@ def main():
summary line contains 'STATUS', 'CHANGES',
'Post-release housekeeping', 'Follow-up' or starts
with '*').''')
-
+
# Parse the arguments
args = parser.parse_args()
diff --git a/tools/dist/security/_gnupg.py b/tools/dist/security/_gnupg.py
index 037dbf3..a19e4e1 100644
--- a/tools/dist/security/_gnupg.py
+++ b/tools/dist/security/_gnupg.py
@@ -1,9 +1,9 @@
# Copyright (c) 2008-2014 by Vinay Sajip.
# All rights reserved.
-#
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
-#
+#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
diff --git a/tools/dist/templates/STATUS.ezt b/tools/dist/templates/STATUS.ezt
new file mode 100644
index 0000000..a896ff4
--- /dev/null
+++ b/tools/dist/templates/STATUS.ezt
@@ -0,0 +1,24 @@
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * THIS RELEASE STREAM IS OPEN FOR STABILIZATION. *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+This file tracks the status of releases in the [major-minor].x line.
+
+See http://subversion.apache.org/docs/community-guide/releasing.html#release-stabilization
+for details on how release lines and voting work, what kinds of bugs can
+delay a release, etc.
+
+Status of [major-minor-patch]:
+
+Candidate changes:
+==================
+
+
+Veto-blocked changes:
+=====================
+
+
+Approved changes:
+=================
diff --git a/tools/dist/templates/download.ezt b/tools/dist/templates/download.ezt
index 19dac3d..3043b2b 100644
--- a/tools/dist/templates/download.ezt
+++ b/tools/dist/templates/download.ezt
@@ -4,12 +4,12 @@
<th>File</th>
<th>Checksum (SHA512)</th>
<th>Signatures</th>
+ <th>PGP Public Keys</th>
</tr>
[for fileinfo]<tr>
<td><a href="[[]preferred]subversion/[fileinfo.filename]">[fileinfo.filename]</a></td>
- <!-- The sha512 line does not have a class="checksum" since the link needn't
- be rendered in monospace. -->
<td>[<a href="https://www.apache.org/dist/subversion/[fileinfo.filename].sha512">SHA-512</a>]</td>
- <td>[<a href="https://www.apache.org/dist/subversion/[fileinfo.filename].asc">PGP</a>]</td>
+ <td>[<a href="https://www.apache.org/dist/subversion/[fileinfo.filename].asc">PGP signatures</a>]</td>
+ <td>[<a href="https://www.apache.org/dist/subversion/subversion-[version].KEYS">PGP keyring</a>]</td>
</tr>[end]
</table>
diff --git a/tools/dist/templates/rc-release-ann.ezt b/tools/dist/templates/rc-release-ann.ezt
index a8bb0f5..ea8e673 100644
--- a/tools/dist/templates/rc-release-ann.ezt
+++ b/tools/dist/templates/rc-release-ann.ezt
@@ -23,6 +23,10 @@ PGP Signatures are available at:
For this release, the following people have provided PGP signatures:
[siginfo]
+These public keys are available at:
+
+ https://www.apache.org/dist/subversion/subversion-[version].KEYS
+
This is a pre-release for what will eventually become version [major-minor-patch] of the
Apache Subversion open source version control system. It may contain known
issues, a complete list of [major-minor-patch]-blocking issues can be found
diff --git a/tools/dist/templates/release-notes-lts.ezt b/tools/dist/templates/release-notes-lts.ezt
new file mode 100644
index 0000000..d28c0cf
--- /dev/null
+++ b/tools/dist/templates/release-notes-lts.ezt
@@ -0,0 +1,251 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Apache Subversion [major-minor] LTS Release Notes</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<style type="text/css">
+ @import url("/style/site.css");
+</style>
+</head>
+
+<body>
+<!--#include virtual="/site-banner.html" -->
+<!--#include virtual="/site-nav.html" -->
+<div id="site-content">
+<!--#include virtual="/site-notice.html" -->
+<!-- **************** BEGIN CONTENT ***************** -->
+
+<!-- ************************************************ -->
+<!-- Sections start with "###" are either templates -->
+<!-- or TODOs. Remove them before release. -->
+<!-- ************************************************ -->
+
+<h1 style="text-align: center">Apache Subversion [major-minor] LTS Release Notes</h1>
+
+<div class="notice">
+<p><span style="color: red"><b>This is work in progress.
+ Subversion [major-minor] has not been released yet.</b></span></p>
+</div>
+
+<div class="h2" id="news">
+<h2>What's New in Apache Subversion [major-minor]
+ <a class="sectionlink" href="#news"
+ title="Link to this section">&para;</a>
+</h2>
+
+<ul>
+ <!-- The main changes...
+ <li><a href="#"
+ >###</a></li>
+ -->
+ <li><a href="#enhancements"
+ >Many enhancements and bug fixes</a></li>
+ <li><a href="#issues"
+ >Known issues in the release</a></li>
+ <!--
+ <li><a href="#troubleshooting"
+ >Troubleshooting issues specific to this release</a></li>
+ -->
+</ul>
+
+<p>Apache Subversion [major-minor] is a superset of all previous Subversion
+releases, and is as of the time of its release considered the current
+"best" release. Any feature or bugfix in 1.0.x through [previous-major-minor].x is also
+in [major-minor], but [major-minor] contains features and bugfixes not present in any
+earlier release.</p>
+
+<p>This page describes only major changes. For a complete list of
+changes, see the [major-minor] section of the <a
+href="https://svn.apache.org/repos/asf/subversion/trunk/CHANGES" >CHANGES</a>
+file.</p>
+
+</div> <!-- news -->
+
+<div class="h2" id="compatibility">
+<h2>Compatibility Concerns
+ <a class="sectionlink" href="#compatibility"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>Older clients and servers interoperate transparently with [major-minor]
+servers and clients. However, some of the new [major-minor] features may not be
+available unless both client and server are the latest version. There are
+also cases where a new feature will work but will run less efficiently if
+the client is new and the server old.</p>
+
+<p>There is <strong>no need</strong> to <a href="http://svnbook.red-bean.com/en/1.8/svn.reposadmin.maint.html#svn.reposadmin.maint.migrate.svnadmin"
+>dump and reload</a> your repositories.
+Subversion [major-minor] servers can read and write to repositories created by
+earlier versions. To upgrade an existing server installation, just install the
+newest libraries and binaries on top of the older ones.</p>
+
+<p>Subversion [major-minor] maintains API/ABI compatibility with earlier
+releases, by only adding new functions, never removing old ones. A
+program written to any previous 1.x API can both compile
+and run using [major-minor] libraries. However, a program written for [major-minor]
+cannot necessarily compile or run against older libraries.</p>
+
+<p>There may be limited cases where the behavior of old APIs has been
+slightly modified from previous releases. These are cases where edge cases
+of the functionality has been deemed buggy, and therefore improved or removed.
+Please consult the
+<a href="https://svn.apache.org/repos/asf/subversion/trunk/notes/api-errata/[major-minor]/"
+>API errata</a> for more detailed information on what these APIs are
+and what impact these changes may have.</p>
+
+<div class="h3" id="new-feature-compatibility-table">
+<h3>New Feature Compatibility Table
+ <a class="sectionlink" href="#new-feature-compatibility-table"
+ title="Link to this section">&para;</a>
+</h3>
+<table border="1">
+ <tr>
+ <th>New Feature</th>
+ <th>Minimum Client<sup>1</sup></th>
+ <th>Minimum Server</th>
+ <th>Minimum Repository</th>
+ <th>Notes</th></tr>
+</table>
+
+</div> <!-- new-feature-compatibility-table -->
+
+<div class="h3" id="wc-upgrade">
+<h3>Upgrading the Working Copy
+ <a class="sectionlink" href="#wc-upgrade"
+ title="Link to this section">&para;</a>
+</h3>
+
+<p>Subversion [major-minor] uses the same working copy format as Subversion 1.8 through [previous-major-minor].</p>
+
+<p>Before using Subversion [major-minor] with an existing Subversion 1.7 or older
+working copy, users will be required to run the <tt>svn upgrade</tt> command
+to upgrade working copy metadata to the new format. This command may take a
+while in some cases, and for some users, it may be more practical to simply
+checkout a new working copy.</p>
+
+<p><strong>Note:</strong> Subversion [major-minor] cannot upgrade working copies that
+a 1.6 client would have refused to operate upon before an <tt>svn cleanup</tt>
+was run (with a 1.6 client). In other words, before upgrading to 1.8 or newer,
+a 1.6
+or older client must be used to run <tt>svn cleanup</tt> on all 1.6 or older
+working copies that require cleanup. Likewise, Subversion [major-minor] cannot upgrade
+corrupt working copies. Unfixable problems can arise from missing or corrupt
+meta-data inside <tt>.svn</tt> directories. Such damage to the working copy
+is permanent, and cannot be fixed even if <tt>svn cleanup</tt> is run prior
+to the upgrade.</p>
+
+<p>If your working copy does not upgrade cleanly, please check out a new one.
+</p>
+
+</div> <!-- wc-upgrade -->
+
+<!-- (This section only makes sense when there are some issues listed in it.)
+<div class="h3" id="compat-misc">
+<h3>Miscellaneous Compatibility Notes
+ <a class="sectionlink" href="#compat-misc"
+ title="Link to this section">&para;</a>
+</h3>
+
+<p>There are some additional specific areas where changes made in this
+release might necessitate further adjustment by administrators or
+users. We'll cover those in this section.</p>
+
+</div> <!- - compat-misc - ->
+-->
+
+</div> <!-- compatibility -->
+
+<div class="h2" id="new-features">
+<h2>New Features
+ <a class="sectionlink" href="#new-features"
+ title="Link to this section">&para;</a>
+</h2>
+
+</div> <!-- new-features -->
+
+<div class="h2" id="enhancements">
+<h2>Enhancements and Bugfixes
+ <a class="sectionlink" href="#enhancements"
+ title="Link to this section">&para;</a>
+</h2>
+
+<!-- Don't need to highlight every bugfix, just major ones which aren't in
+ any patch release. -->
+
+<div class="h3" id="cmdline">
+<h3>Command-line client improvements (<em>client</em>)
+ <a class="sectionlink" href="#cmdline"
+ title="Link to this section">&para;</a>
+</h3>
+
+</div> <!-- cmdline -->
+
+<div class="h3" id="server-side-improvements">
+<h3>Server-side improvements
+ <a class="sectionlink" href="#server-side-improvements"
+ title="Link to this section">&para;</a>
+</h3>
+
+</div> <!-- server-side-improvements -->
+
+<div class="h3" id="client-server-improvements">
+<h3>Client- and server-side improvements
+ <a class="sectionlink" href="#client-server-improvements"
+ title="Link to this section">&para;</a>
+</h3>
+
+</div> <!-- client-server-improvements -->
+
+</div> <!-- enhancements -->
+
+<div class="h2" id="issues">
+<h2>Known issues in the release
+ <a class="sectionlink" href="#issues"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>There are no known issues specific to this release at the moment.</p>
+
+<!--
+<p>There are some known issues in the Subversion [major-minor] releases. These
+may be fixed in later [major-minor].x releases.</p>
+-->
+
+</div> <!-- issues -->
+
+<!-- (This section only makes sense when there are some issues listed in it.)
+<div class="h2" id="troubleshooting">
+<h2>Troubleshooting issues specific to this release
+ <a class="sectionlink" href="#troubleshooting"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>Subversion [major-minor] introduces new features and makes use of new techniques
+which can trigger problems not encountered in previous versions. In contrast to
+known issues, things listed here are not due to some bug or issue in Subversion
+itself and therefore cannot be fixed with a new patch release.
+This section lists all known problems and provides instructions to solve them,
+if they occur.</p>
+
+<p>There are no known issues specific to this release at the moment.</p>
+
+</div> <!- - troubleshooting - ->
+-->
+
+<div class="h2" id="lts-release">
+<h2>Subversion [major-minor].x is a Long-Term Support (LTS) Release
+ <a class="sectionlink" href="#lts-release"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>[major-minor] is a Long-Term Support (LTS) release.
+ See <a href="/docs/release-notes/#supported-versions">Supported Versions</a>
+ and <a href="/roadmap.html#release-planning">How We Plan Releases</a>.</p>
+
+</div> <!-- lts-release -->
+
+<!-- ***************** END CONTENT ****************** -->
+</div> <!-- #site-content -->
+</body>
+</html>
diff --git a/tools/dist/templates/release-notes.ezt b/tools/dist/templates/release-notes.ezt
new file mode 100644
index 0000000..b07193d
--- /dev/null
+++ b/tools/dist/templates/release-notes.ezt
@@ -0,0 +1,251 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Apache Subversion [major-minor] Release Notes</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<style type="text/css">
+ @import url("/style/site.css");
+</style>
+</head>
+
+<body>
+<!--#include virtual="/site-banner.html" -->
+<!--#include virtual="/site-nav.html" -->
+<div id="site-content">
+<!--#include virtual="/site-notice.html" -->
+<!-- **************** BEGIN CONTENT ***************** -->
+
+<!-- ************************************************ -->
+<!-- Sections start with "###" are either templates -->
+<!-- or TODOs. Remove them before release. -->
+<!-- ************************************************ -->
+
+<h1 style="text-align: center">Apache Subversion [major-minor] Release Notes</h1>
+
+<div class="notice">
+<p><span style="color: red"><b>This is work in progress.
+ Subversion [major-minor] has not been released yet.</b></span></p>
+</div>
+
+<div class="h2" id="news">
+<h2>What's New in Apache Subversion [major-minor]
+ <a class="sectionlink" href="#news"
+ title="Link to this section">&para;</a>
+</h2>
+
+<ul>
+ <!-- The main changes...
+ <li><a href="#"
+ >###</a></li>
+ -->
+ <li><a href="#enhancements"
+ >Many enhancements and bug fixes</a></li>
+ <li><a href="#issues"
+ >Known issues in the release</a></li>
+ <!--
+ <li><a href="#troubleshooting"
+ >Troubleshooting issues specific to this release</a></li>
+ -->
+</ul>
+
+<p>Apache Subversion [major-minor] is a superset of all previous Subversion
+releases, and is as of the time of its release considered the current
+"best" release. Any feature or bugfix in 1.0.x through [previous-major-minor].x is also
+in [major-minor], but [major-minor] contains features and bugfixes not present in any
+earlier release.</p>
+
+<p>This page describes only major changes. For a complete list of
+changes, see the [major-minor] section of the <a
+href="https://svn.apache.org/repos/asf/subversion/trunk/CHANGES" >CHANGES</a>
+file.</p>
+
+</div> <!-- news -->
+
+<div class="h2" id="compatibility">
+<h2>Compatibility Concerns
+ <a class="sectionlink" href="#compatibility"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>Older clients and servers interoperate transparently with [major-minor]
+servers and clients. However, some of the new [major-minor] features may not be
+available unless both client and server are the latest version. There are
+also cases where a new feature will work but will run less efficiently if
+the client is new and the server old.</p>
+
+<p>There is <strong>no need</strong> to <a href="http://svnbook.red-bean.com/en/1.8/svn.reposadmin.maint.html#svn.reposadmin.maint.migrate.svnadmin"
+>dump and reload</a> your repositories.
+Subversion [major-minor] servers can read and write to repositories created by
+earlier versions. To upgrade an existing server installation, just install the
+newest libraries and binaries on top of the older ones.</p>
+
+<p>Subversion [major-minor] maintains API/ABI compatibility with earlier
+releases, by only adding new functions, never removing old ones. A
+program written to any previous 1.x API can both compile
+and run using [major-minor] libraries. However, a program written for [major-minor]
+cannot necessarily compile or run against older libraries.</p>
+
+<p>There may be limited cases where the behavior of old APIs has been
+slightly modified from previous releases. These are cases where edge cases
+of the functionality has been deemed buggy, and therefore improved or removed.
+Please consult the
+<a href="https://svn.apache.org/repos/asf/subversion/trunk/notes/api-errata/[major-minor]/"
+>API errata</a> for more detailed information on what these APIs are
+and what impact these changes may have.</p>
+
+<div class="h3" id="new-feature-compatibility-table">
+<h3>New Feature Compatibility Table
+ <a class="sectionlink" href="#new-feature-compatibility-table"
+ title="Link to this section">&para;</a>
+</h3>
+<table border="1">
+ <tr>
+ <th>New Feature</th>
+ <th>Minimum Client<sup>1</sup></th>
+ <th>Minimum Server</th>
+ <th>Minimum Repository</th>
+ <th>Notes</th></tr>
+</table>
+
+</div> <!-- new-feature-compatibility-table -->
+
+<div class="h3" id="wc-upgrade">
+<h3>Upgrading the Working Copy
+ <a class="sectionlink" href="#wc-upgrade"
+ title="Link to this section">&para;</a>
+</h3>
+
+<p>Subversion [major-minor] uses the same working copy format as Subversion 1.8 through [previous-major-minor].</p>
+
+<p>Before using Subversion [major-minor] with an existing Subversion 1.7 or older
+working copy, users will be required to run the <tt>svn upgrade</tt> command
+to upgrade working copy metadata to the new format. This command may take a
+while in some cases, and for some users, it may be more practical to simply
+checkout a new working copy.</p>
+
+<p><strong>Note:</strong> Subversion [major-minor] cannot upgrade working copies that
+a 1.6 client would have refused to operate upon before an <tt>svn cleanup</tt>
+was run (with a 1.6 client). In other words, before upgrading to 1.8 or newer,
+a 1.6
+or older client must be used to run <tt>svn cleanup</tt> on all 1.6 or older
+working copies that require cleanup. Likewise, Subversion [major-minor] cannot upgrade
+corrupt working copies. Unfixable problems can arise from missing or corrupt
+meta-data inside <tt>.svn</tt> directories. Such damage to the working copy
+is permanent, and cannot be fixed even if <tt>svn cleanup</tt> is run prior
+to the upgrade.</p>
+
+<p>If your working copy does not upgrade cleanly, please check out a new one.
+</p>
+
+</div> <!-- wc-upgrade -->
+
+<!-- (This section only makes sense when there are some issues listed in it.)
+<div class="h3" id="compat-misc">
+<h3>Miscellaneous Compatibility Notes
+ <a class="sectionlink" href="#compat-misc"
+ title="Link to this section">&para;</a>
+</h3>
+
+<p>There are some additional specific areas where changes made in this
+release might necessitate further adjustment by administrators or
+users. We'll cover those in this section.</p>
+
+</div> <!- - compat-misc - ->
+-->
+
+</div> <!-- compatibility -->
+
+<div class="h2" id="new-features">
+<h2>New Features
+ <a class="sectionlink" href="#new-features"
+ title="Link to this section">&para;</a>
+</h2>
+
+</div> <!-- new-features -->
+
+<div class="h2" id="enhancements">
+<h2>Enhancements and Bugfixes
+ <a class="sectionlink" href="#enhancements"
+ title="Link to this section">&para;</a>
+</h2>
+
+<!-- Don't need to highlight every bugfix, just major ones which aren't in
+ any patch release. -->
+
+<div class="h3" id="cmdline">
+<h3>Command-line client improvements (<em>client</em>)
+ <a class="sectionlink" href="#cmdline"
+ title="Link to this section">&para;</a>
+</h3>
+
+</div> <!-- cmdline -->
+
+<div class="h3" id="server-side-improvements">
+<h3>Server-side improvements
+ <a class="sectionlink" href="#server-side-improvements"
+ title="Link to this section">&para;</a>
+</h3>
+
+</div> <!-- server-side-improvements -->
+
+<div class="h3" id="client-server-improvements">
+<h3>Client- and server-side improvements
+ <a class="sectionlink" href="#client-server-improvements"
+ title="Link to this section">&para;</a>
+</h3>
+
+</div> <!-- client-server-improvements -->
+
+</div> <!-- enhancements -->
+
+<div class="h2" id="issues">
+<h2>Known issues in the release
+ <a class="sectionlink" href="#issues"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>There are no known issues specific to this release at the moment.</p>
+
+<!--
+<p>There are some known issues in the Subversion [major-minor] releases. These
+may be fixed in later [major-minor].x releases.</p>
+-->
+
+</div> <!-- issues -->
+
+<!-- (This section only makes sense when there are some issues listed in it.)
+<div class="h2" id="troubleshooting">
+<h2>Troubleshooting issues specific to this release
+ <a class="sectionlink" href="#troubleshooting"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>Subversion [major-minor] introduces new features and makes use of new techniques
+which can trigger problems not encountered in previous versions. In contrast to
+known issues, things listed here are not due to some bug or issue in Subversion
+itself and therefore cannot be fixed with a new patch release.
+This section lists all known problems and provides instructions to solve them,
+if they occur.</p>
+
+<p>There are no known issues specific to this release at the moment.</p>
+
+</div> <!- - troubleshooting - ->
+-->
+
+<div class="h2" id="non-lts-release">
+<h2>Subversion [major-minor].x is a Regular Release
+ <a class="sectionlink" href="#non-lts-release"
+ title="Link to this section">&para;</a>
+</h2>
+
+<p>[major-minor] is a regular release, not a Long-Term Support release.
+ See <a href="/docs/release-notes/#supported-versions">Supported Versions</a>
+ and <a href="/roadmap.html#release-planning">How We Plan Releases</a>.</p>
+
+</div> <!-- non-lts-release -->
+
+<!-- ***************** END CONTENT ****************** -->
+</div> <!-- #site-content -->
+</body>
+</html>
diff --git a/tools/dist/templates/stable-release-ann.ezt b/tools/dist/templates/stable-release-ann.ezt
index dea8c88..06a8cad 100644
--- a/tools/dist/templates/stable-release-ann.ezt
+++ b/tools/dist/templates/stable-release-ann.ezt
@@ -34,6 +34,10 @@ PGP Signatures are available at:
For this release, the following people have provided PGP signatures:
[siginfo]
+These public keys are available at:
+
+ https://www.apache.org/dist/subversion/subversion-[version].KEYS
+
Release notes for the [major-minor].x release series may be found at:
https://subversion.apache.org/docs/release-notes/[major-minor].html
diff --git a/tools/examples/svnlook.py b/tools/examples/svnlook.py
index c289aeb..a576d3f 100755
--- a/tools/examples/svnlook.py
+++ b/tools/examples/svnlook.py
@@ -21,7 +21,7 @@
# under the License.
######################################################################
#
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/examples/svnlook.py $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/examples/svnlook.py $
# $LastChangedDate: 2013-11-14 11:11:07 +0000 (Thu, 14 Nov 2013) $
# $LastChangedRevision: 1541878 $
diff --git a/tools/examples/walk-config-auth.py b/tools/examples/walk-config-auth.py
index 5841b6c..f31bf03 100755
--- a/tools/examples/walk-config-auth.py
+++ b/tools/examples/walk-config-auth.py
@@ -18,7 +18,7 @@ credentials found.
""" % (sys.argv[0]))
sys.exit(0)
-config_dir = svn.core.svn_config_get_user_config_path(None, '')
+config_dir = svn.core.svn_config_get_user_config_path(None, None)
if len(sys.argv) > 1:
config_dir = sys.argv[1]
diff --git a/tools/hook-scripts/commit-access-control.pl.in b/tools/hook-scripts/commit-access-control.pl.in
index ceb5e73..9d772e0 100755
--- a/tools/hook-scripts/commit-access-control.pl.in
+++ b/tools/hook-scripts/commit-access-control.pl.in
@@ -6,7 +6,7 @@
# commit in repository REPOS using the permissions listed in the
# configuration file CONF_FILE.
#
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/hook-scripts/commit-access-control.pl.in $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/hook-scripts/commit-access-control.pl.in $
# $LastChangedDate: 2009-11-16 19:07:17 +0000 (Mon, 16 Nov 2009) $
# $LastChangedBy: hwright $
# $LastChangedRevision: 880911 $
diff --git a/tools/hook-scripts/mailer/mailer.conf.example b/tools/hook-scripts/mailer/mailer.conf.example
index 3887e6b..1a81c48 100644
--- a/tools/hook-scripts/mailer/mailer.conf.example
+++ b/tools/hook-scripts/mailer/mailer.conf.example
@@ -1,7 +1,7 @@
#
# mailer.conf: example configuration file for mailer.py
#
-# $Id: mailer.conf.example 1777846 2017-01-07 19:35:49Z julianfoad $
+# $Id: mailer.conf.example 1872403 2020-01-07 01:08:27Z futatuki $
[general]
@@ -23,6 +23,11 @@
# This option specifies the hostname for delivery via SMTP.
#smtp_hostname = localhost
+# This option specifies the TCP port number to connect for SMTP.
+# If it is not specified, 25 is used for SMTP and 465 is used for
+# SMTP-Over-SSL by default.
+#smtp_port = 25
+
# Username and password for SMTP servers requiring authorisation.
#smtp_username = example
#smtp_password = example
diff --git a/tools/hook-scripts/mailer/mailer.py b/tools/hook-scripts/mailer/mailer.py
index b3045f3..09a6f75 100755
--- a/tools/hook-scripts/mailer/mailer.py
+++ b/tools/hook-scripts/mailer/mailer.py
@@ -22,10 +22,10 @@
#
# mailer.py: send email describing a commit
#
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/hook-scripts/mailer/mailer.py $
-# $LastChangedDate: 2018-02-18 19:06:38 +0000 (Sun, 18 Feb 2018) $
-# $LastChangedBy: danielsh $
-# $LastChangedRevision: 1824690 $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/hook-scripts/mailer/mailer.py $
+# $LastChangedDate: 2020-01-07 01:08:27 +0000 (Tue, 07 Jan 2020) $
+# $LastChangedBy: futatuki $
+# $LastChangedRevision: 1872403 $
#
# USAGE: mailer.py commit REPOS REVISION [CONFIG-FILE]
# mailer.py propchange REPOS REVISION AUTHOR REVPROPNAME [CONFIG-FILE]
@@ -123,7 +123,7 @@ def main(pool, cmd, config_fname, repos_dir, cmd_args):
else:
raise UnknownSubcommand(cmd)
- messenger.generate()
+ return messenger.generate()
def remove_leading_slashes(path):
@@ -285,15 +285,73 @@ class SMTPOutput(MailedOutput):
self.write(self.mail_headers(group, params))
def finish(self):
- if self.cfg.is_set('general.smtp_ssl') and self.cfg.general.smtp_ssl == 'yes':
- server = smtplib.SMTP_SSL(self.cfg.general.smtp_hostname)
+ """
+ Send email via SMTP or SMTP_SSL, logging in if username is
+ specified.
+
+ Errors such as invalid recipient, which affect a particular email,
+ are reported to stderr and raise MessageSendFailure. If the caller
+ has other emails to send, it may continue doing so.
+
+ Errors caused by bad configuration, such as login failures, for
+ which too many occurrences could lead to SMTP server lockout, are
+ reported to stderr and re-raised. These should be considered fatal
+ (to minimize the chances of said lockout).
+ """
+
+ if self.cfg.is_set('general.smtp_port'):
+ smtp_port = self.cfg.general.smtp_port
else:
- server = smtplib.SMTP(self.cfg.general.smtp_hostname)
- if self.cfg.is_set('general.smtp_username'):
- server.login(self.cfg.general.smtp_username,
- self.cfg.general.smtp_password)
- server.sendmail(self.from_addr, self.to_addrs, self.buffer.getvalue())
- server.quit()
+ smtp_port = 0
+ try:
+ if self.cfg.is_set('general.smtp_ssl') and self.cfg.general.smtp_ssl == 'yes':
+ server = smtplib.SMTP_SSL(self.cfg.general.smtp_hostname, smtp_port)
+ else:
+ server = smtplib.SMTP(self.cfg.general.smtp_hostname, smtp_port)
+ except Exception as detail:
+ sys.stderr.write("mailer.py: Failed to instantiate SMTP object: %s\n" % (detail,))
+ # Any error to instantiate is fatal
+ raise
+
+ try:
+ if self.cfg.is_set('general.smtp_username'):
+ try:
+ server.login(self.cfg.general.smtp_username,
+ self.cfg.general.smtp_password)
+ except smtplib.SMTPException as detail:
+ sys.stderr.write("mailer.py: SMTP login failed with username %s and/or password: %s\n"
+ % (self.cfg.general.smtp_username, detail,))
+ # Any error at login is fatal
+ raise
+
+ server.sendmail(self.from_addr, self.to_addrs, self.buffer.getvalue())
+
+ ### TODO: 'raise .. from' is Python 3+. When we convert this
+ ### script to Python 3, uncomment 'from detail' below
+ ### (2 instances):
+
+ except smtplib.SMTPRecipientsRefused as detail:
+ sys.stderr.write("mailer.py: SMTP recipient(s) refused: %s: %s\n"
+ % (self.to_addrs, detail,))
+ raise MessageSendFailure ### from detail
+
+ except smtplib.SMTPSenderRefused as detail:
+ sys.stderr.write("mailer.py: SMTP sender refused: %s: %s\n"
+ % (self.from_addr, detail,))
+ raise MessageSendFailure ### from detail
+
+ except smtplib.SMTPException as detail:
+ # All other errors are fatal; this includes:
+ # SMTPHeloError, SMTPDataError, SMTPNotSupportedError
+ sys.stderr.write("mailer.py: SMTP error occurred: %s\n" % (detail,))
+ raise
+
+ finally:
+ try:
+ server.quit()
+ except smtplib.SMTPException as detail:
+ sys.stderr.write("mailer.py: Error occurred during SMTP session cleanup: %s\n"
+ % (detail,))
class StandardOutput(OutputBase):
@@ -421,21 +479,26 @@ class Commit(Messenger):
### rather than rebuilding it each time.
subpool = svn.core.svn_pool_create(self.pool)
+ ret = 0
# build a renderer, tied to our output stream
renderer = TextCommitRenderer(self.output)
for (group, param_tuple), (params, paths) in self.groups.items():
- self.output.start(group, params)
+ try:
+ self.output.start(group, params)
- # generate the content for this group and set of params
- generate_content(renderer, self.cfg, self.repos, self.changelist,
- group, params, paths, subpool)
+ # generate the content for this group and set of params
+ generate_content(renderer, self.cfg, self.repos, self.changelist,
+ group, params, paths, subpool)
- self.output.finish()
+ self.output.finish()
+ except MessageSendFailure:
+ ret = 1
svn.core.svn_pool_clear(subpool)
svn.core.svn_pool_destroy(subpool)
+ return ret
class PropChange(Messenger):
@@ -456,35 +519,40 @@ class PropChange(Messenger):
def generate(self):
actions = { 'A': 'added', 'M': 'modified', 'D': 'deleted' }
+ ret = 0
for (group, param_tuple), params in self.groups.items():
- self.output.start(group, params)
- self.output.write('Author: %s\n'
- 'Revision: %s\n'
- 'Property Name: %s\n'
- 'Action: %s\n'
- '\n'
- % (self.author, self.repos.rev, self.propname,
- actions.get(self.action, 'Unknown (\'%s\')' \
- % self.action)))
- if self.action == 'A' or self.action not in actions:
- self.output.write('Property value:\n')
- propvalue = self.repos.get_rev_prop(self.propname)
- self.output.write(propvalue)
- elif self.action == 'M':
- self.output.write('Property diff:\n')
- tempfile1 = tempfile.NamedTemporaryFile()
- tempfile1.write(sys.stdin.read())
- tempfile1.flush()
- tempfile2 = tempfile.NamedTemporaryFile()
- tempfile2.write(self.repos.get_rev_prop(self.propname))
- tempfile2.flush()
- self.output.run(self.cfg.get_diff_cmd(group, {
- 'label_from' : 'old property value',
- 'label_to' : 'new property value',
- 'from' : tempfile1.name,
- 'to' : tempfile2.name,
- }))
- self.output.finish()
+ try:
+ self.output.start(group, params)
+ self.output.write('Author: %s\n'
+ 'Revision: %s\n'
+ 'Property Name: %s\n'
+ 'Action: %s\n'
+ '\n'
+ % (self.author, self.repos.rev, self.propname,
+ actions.get(self.action, 'Unknown (\'%s\')' \
+ % self.action)))
+ if self.action == 'A' or self.action not in actions:
+ self.output.write('Property value:\n')
+ propvalue = self.repos.get_rev_prop(self.propname)
+ self.output.write(propvalue)
+ elif self.action == 'M':
+ self.output.write('Property diff:\n')
+ tempfile1 = tempfile.NamedTemporaryFile()
+ tempfile1.write(sys.stdin.read())
+ tempfile1.flush()
+ tempfile2 = tempfile.NamedTemporaryFile()
+ tempfile2.write(self.repos.get_rev_prop(self.propname))
+ tempfile2.flush()
+ self.output.run(self.cfg.get_diff_cmd(group, {
+ 'label_from' : 'old property value',
+ 'label_to' : 'new property value',
+ 'from' : tempfile1.name,
+ 'to' : tempfile2.name,
+ }))
+ self.output.finish()
+ except MessageSendFailure:
+ ret = 1
+ return ret
def get_commondir(dirlist):
@@ -564,21 +632,26 @@ class Lock(Messenger):
self.dirlist[0], self.pool)
def generate(self):
+ ret = 0
for (group, param_tuple), (params, paths) in self.groups.items():
- self.output.start(group, params)
+ try:
+ self.output.start(group, params)
- self.output.write('Author: %s\n'
- '%s paths:\n' %
- (self.author, self.do_lock and 'Locked' or 'Unlocked'))
+ self.output.write('Author: %s\n'
+ '%s paths:\n' %
+ (self.author, self.do_lock and 'Locked' or 'Unlocked'))
- self.dirlist.sort()
- for dir in self.dirlist:
- self.output.write(' %s\n\n' % dir)
+ self.dirlist.sort()
+ for dir in self.dirlist:
+ self.output.write(' %s\n\n' % dir)
- if self.do_lock:
- self.output.write('Comment:\n%s\n' % (self.lock.comment or ''))
+ if self.do_lock:
+ self.output.write('Comment:\n%s\n' % (self.lock.comment or ''))
- self.output.finish()
+ self.output.finish()
+ except MessageSendFailure:
+ ret = 1
+ return ret
class DiffSelections:
@@ -1394,6 +1467,8 @@ class UnknownMappingSpec(Exception):
pass
class UnknownSubcommand(Exception):
pass
+class MessageSendFailure(Exception):
+ pass
if __name__ == '__main__':
@@ -1455,8 +1530,9 @@ if the property was added, modified or deleted, respectively.
if not os.path.exists(config_fname):
raise MissingConfig(config_fname)
- svn.core.run_app(main, cmd, config_fname, repos_dir,
- sys.argv[3:3+expected_args])
+ ret = svn.core.run_app(main, cmd, config_fname, repos_dir,
+ sys.argv[3:3+expected_args])
+ sys.exit(1 if ret else 0)
# ------------------------------------------------------------------------
# TODO
diff --git a/tools/hook-scripts/svn2feed.py b/tools/hook-scripts/svn2feed.py
index e2fe2f3..9e21f9c 100755
--- a/tools/hook-scripts/svn2feed.py
+++ b/tools/hook-scripts/svn2feed.py
@@ -70,7 +70,7 @@ Options:
# is actually set only on initial feed creation, and thereafter simply
# re-used from the pickle each time.
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/hook-scripts/svn2feed.py $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/hook-scripts/svn2feed.py $
# $LastChangedDate: 2016-04-30 08:16:53 +0000 (Sat, 30 Apr 2016) $
# $LastChangedBy: stefan2 $
# $LastChangedRevision: 1741723 $
diff --git a/tools/hook-scripts/svnperms.py b/tools/hook-scripts/svnperms.py
index f029db0..7655ca5 100755
--- a/tools/hook-scripts/svnperms.py
+++ b/tools/hook-scripts/svnperms.py
@@ -21,7 +21,7 @@
#
#
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/hook-scripts/svnperms.py $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/hook-scripts/svnperms.py $
# $LastChangedDate: 2016-04-30 08:16:53 +0000 (Sat, 30 Apr 2016) $
# $LastChangedBy: stefan2 $
# $LastChangedRevision: 1741723 $
diff --git a/tools/hook-scripts/validate-files.py b/tools/hook-scripts/validate-files.py
index 7169251..7f58ad8 100755
--- a/tools/hook-scripts/validate-files.py
+++ b/tools/hook-scripts/validate-files.py
@@ -19,7 +19,13 @@
"""Subversion pre-commit hook script that runs user configured commands
to validate files in the commit and reject the commit if the commands
exit with a non-zero exit code. The script expects a validate-files.conf
-file placed in the conf dir under the repo the commit is for."""
+file placed in the conf dir under the repo the commit is for.
+
+Note: As changed file paths $FILE are always represented as a Unicode (Py3)
+ or UTF-8 (Py2) strings, you might need to set apropriate locale and
+ PYTHONIOENCODING environment variable for this script and
+ commands to handle non-ascii path and command outputs, especially
+ you want to use svnlook cat command to inspect file contents."""
import sys
import os
@@ -30,11 +36,13 @@ import fnmatch
try:
# Python >= 3.0
import configparser
+ ConfigParser = configparser.ConfigParser
except ImportError:
# Python < 3.0
import ConfigParser as configparser
+ ConfigParser = configparser.SafeConfigParser
-class Config(configparser.SafeConfigParser):
+class Config(ConfigParser):
"""Superclass of SafeConfigParser with some customizations
for this script"""
def optionxform(self, option):
@@ -80,18 +88,26 @@ class Commands:
line = p.stdout.readline()
if not line:
break
- line = line.decode().strip()
+ line = line.strip()
text_mod = line[0:1]
# Only if the contents of the file changed (by addition or update)
# directories always end in / in the svnlook changed output
- if line[-1] != "/" and (text_mod == "A" or text_mod == "U"):
- changed.append(line[4:])
+ if line[-1:] != b"/" and (text_mod == b"A" or text_mod == b"U"):
+ changed_path = line[4:]
+ if not isinstance(changed_path, str):
+ # svnlook always uses UTF-8 for internal path
+ changed_path = changed_path.decode('utf-8')
+ changed.append(changed_path)
# wait on the command to finish so we can get the
# returncode/stderr output
data = p.communicate()
if p.returncode != 0:
- sys.stderr.write(data[1].decode())
+ err_mesg = data[1]
+ if sys.stderr.encoding:
+ err_mesg =err_mesg.decode(sys.stderr.encoding,
+ 'backslashreplace')
+ sys.stderr.write(err_mesg)
sys.exit(2)
return changed
@@ -109,7 +125,11 @@ class Commands:
cmd_env['FILE'] = fn
p = subprocess.Popen(cmd, shell=True, env=cmd_env, stderr=subprocess.PIPE)
data = p.communicate()
- return (p.returncode, data[1].decode())
+ err_mesg = data[1]
+ if sys.stderr.encoding:
+ err_mesg = err_mesg.decode(sys.stderr.encoding,
+ 'backslashreplace')
+ return (p.returncode, err_mesg)
def main(repo, txn):
exitcode = 0
diff --git a/tools/server-side/fsfs-reshard.py b/tools/server-side/fsfs-reshard.py
index d491c51..025e866 100755
--- a/tools/server-side/fsfs-reshard.py
+++ b/tools/server-side/fsfs-reshard.py
@@ -46,7 +46,7 @@
# under the License.
# ====================================================================
#
-# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.13.x/tools/server-side/fsfs-reshard.py $
+# $HeadURL: https://svn.apache.org/repos/asf/subversion/branches/1.14.x/tools/server-side/fsfs-reshard.py $
# $LastChangedDate: 2016-04-30 08:16:53 +0000 (Sat, 30 Apr 2016) $
# $LastChangedBy: stefan2 $
# $LastChangedRevision: 1741723 $
diff --git a/tools/server-side/svn-backup-dumps.py b/tools/server-side/svn-backup-dumps.py
index 2f3a231..e7a1a26 100755
--- a/tools/server-side/svn-backup-dumps.py
+++ b/tools/server-side/svn-backup-dumps.py
@@ -377,38 +377,48 @@ class SvnBackup:
return self.exec_cmd_unix(cmd, output, printerr)
def exec_cmd_unix(self, cmd, output=None, printerr=False):
+ if printerr:
+ if sys.hexversion >= 0x3000000:
+ sys.stdout.flush()
+ errout = sys.stdout.buffer
+ else:
+ errout = sys.stdout
+ else:
+ errout = PIPE
try:
- proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=False)
+ proc = Popen(cmd, stdout=PIPE, stderr=errout, shell=False)
except:
return (256, "", "Popen failed (%s ...):\n %s" % (cmd[0],
str(sys.exc_info()[1])))
- stdout = proc.stdout
- stderr = proc.stderr
- self.set_nonblock(stdout)
- self.set_nonblock(stderr)
- readfds = [ stdout, stderr ]
- selres = select.select(readfds, [], [])
- bufout = ""
- buferr = ""
- while len(selres[0]) > 0:
- for fd in selres[0]:
- buf = fd.read(16384)
- if len(buf) == 0:
- readfds.remove(fd)
- elif fd == stdout:
- if output:
+ if output is None:
+ bufout, buferr = proc.communicate()
+ rc = proc.returncode
+ if buferr is None:
+ buferr = b""
+ else:
+ stdout = proc.stdout
+ self.set_nonblock(stdout)
+ readfds = [ stdout ]
+ if not printerr:
+ stderr = proc.stderr
+ self.set_nonblock(stderr)
+ readfds.append(stderr)
+ selres = select.select(readfds, [], [])
+ bufout = b""
+ buferr = b""
+ while len(selres[0]) > 0:
+ for fd in selres[0]:
+ buf = fd.read(16384)
+ if len(buf) == 0:
+ readfds.remove(fd)
+ elif fd == stdout:
output.write(buf)
else:
- bufout += buf
- else:
- if printerr:
- sys.stdout.write("%s " % buf)
- else:
buferr += buf
- if len(readfds) == 0:
- break
- selres = select.select(readfds, [], [])
- rc = proc.wait()
+ if len(readfds) == 0:
+ break
+ selres = select.select(readfds, [], [])
+ rc = proc.wait()
if printerr:
print("")
return (rc, bufout, buferr)
@@ -420,8 +430,8 @@ class SvnBackup:
return (256, "", "Popen failed (%s ...):\n %s" % (cmd[0],
str(sys.exc_info()[1])))
stdout = proc.stdout
- bufout = ""
- buferr = ""
+ bufout = b""
+ buferr = b""
buf = stdout.read(16384)
while len(buf) > 0:
if output: