From 5bcb3deb2fd200aad82793591eac246c053551e0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 28 Apr 2026 14:53:29 +1000 Subject: [PATCH] packaging: remove old release system --- packaging/branch-from-patch | 174 ---------------- packaging/patch-update | 244 ----------------------- packaging/release-rsync | 385 ------------------------------------ 3 files changed, 803 deletions(-) delete mode 100755 packaging/branch-from-patch delete mode 100755 packaging/patch-update delete mode 100755 packaging/release-rsync diff --git a/packaging/branch-from-patch b/packaging/branch-from-patch deleted file mode 100755 index 40e5653c4..000000000 --- a/packaging/branch-from-patch +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env -S python3 -B - -# This script turns one or more diff files in the patches dir (which is -# expected to be a checkout of the rsync-patches git repo) into a branch -# in the main rsync git checkout. This allows the applied patch to be -# merged with the latest rsync changes and tested. To update the diff -# with the resulting changes, see the patch-update script. - -import os, sys, re, argparse, glob - -sys.path = ['packaging'] + sys.path - -from pkglib import * - -def main(): - global created, info, local_branch - - cur_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir) - - local_branch = get_patch_branches(args.base_branch) - - if args.delete_local_branches: - for name in sorted(local_branch): - branch = f"patch/{args.base_branch}/{name}" - cmd_chk(['git', 'branch', '-D', branch]) - local_branch = set() - - if args.add_missing: - for fn in sorted(glob.glob(f"{args.patches_dir}/*.diff")): - name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn)) - if name not in local_branch and fn not in args.patch_files: - args.patch_files.append(fn) - - if not args.patch_files: - return - - for fn in args.patch_files: - if not fn.endswith('.diff'): - die(f"Filename is not a .diff file: {fn}") - if not os.path.isfile(fn): - die(f"File not found: {fn}") - - scanned = set() - info = { } - - patch_list = [ ] - for fn in args.patch_files: - m = re.match(r'^(?P.*?)(?P[^/]+)\.diff$', fn) - patch = argparse.Namespace(**m.groupdict()) - if patch.name in scanned: - continue - patch.fn = fn - - lines = [ ] - commit_hash = None - with open(patch.fn, 'r', encoding='utf-8') as fh: - for line in fh: - m = re.match(r'^based-on: (\S+)', line) - if m: - commit_hash = m[1] - break - if (re.match(r'^index .*\.\..* \d', line) - or re.match(r'^diff --git ', line) - or re.match(r'^--- (old|a)/', line)): - break - lines.append(re.sub(r'\s*\Z', "\n", line, 1)) - info_txt = ''.join(lines).strip() + "\n" - lines = None - - parent = args.base_branch - patches = re.findall(r'patch -p1 <%s/(\S+)\.diff' % args.patches_dir, info_txt) - if patches: - last = patches.pop() - if last != patch.name: - warn(f"No identity patch line in {patch.fn}") - patches.append(last) - if patches: - parent = patches.pop() - if parent not in scanned: - diff_fn = patch.dir + parent + '.diff' - if not os.path.isfile(diff_fn): - die(f"Failed to find parent of {patch.fn}: {parent}") - # Add parent to args.patch_files so that we will look for the - # parent's parent. Any duplicates will be ignored. - args.patch_files.append(diff_fn) - else: - warn(f"No patch lines found in {patch.fn}") - - info[patch.name] = [ parent, info_txt, commit_hash ] - - patch_list.append(patch) - - created = set() - for patch in patch_list: - create_branch(patch) - - cmd_chk(['git', 'checkout', args.base_branch]) - - -def create_branch(patch): - if patch.name in created: - return - created.add(patch.name) - - parent, info_txt, commit_hash = info[patch.name] - parent = argparse.Namespace(dir=patch.dir, name=parent, fn=patch.dir + parent + '.diff') - - if parent.name == args.base_branch: - parent_branch = commit_hash if commit_hash else args.base_branch - else: - create_branch(parent) - parent_branch = '/'.join(['patch', args.base_branch, parent.name]) - - branch = '/'.join(['patch', args.base_branch, patch.name]) - print("\n" + '=' * 64) - print(f"Processing {branch} ({parent_branch})") - - if patch.name in local_branch: - cmd_chk(['git', 'branch', '-D', branch]) - - cmd_chk(['git', 'checkout', '-b', branch, parent_branch]) - - info_fn = 'PATCH.' + patch.name - with open(info_fn, 'w', encoding='utf-8') as fh: - fh.write(info_txt) - cmd_chk(['git', 'add', info_fn]) - - with open(patch.fn, 'r', encoding='utf-8') as fh: - patch_txt = fh.read() - - cmd_run('patch -p1'.split(), input=patch_txt) - - for fn in glob.glob('*.orig') + glob.glob('*/*.orig'): - os.unlink(fn) - - pos = 0 - new_file_re = re.compile(r'\nnew file mode (?P\d+)\s+--- /dev/null\s+\+\+\+ b/(?P.+)') - while True: - m = new_file_re.search(patch_txt, pos) - if not m: - break - os.chmod(m['fn'], int(m['mode'], 8)) - cmd_chk(['git', 'add', m['fn']]) - pos = m.end() - - while True: - cmd_chk('git status'.split()) - ans = input('Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ') - if ans == '': - break - cmd_chk("git add " + ans, shell=True) - - while True: - s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."]) - if not s.returncode: - break - s = cmd_run([os.environ.get('SHELL', '/bin/sh')]) - if s.returncode: - die('Aborting due to shell error code') - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Create a git patch branch from an rsync patch file.", add_help=False) - parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.") - parser.add_argument('--add-missing', '-a', action='store_true', help="Add a branch for every patches/*.diff that doesn't have a branch.") - parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.") - parser.add_argument('--delete', dest='delete_local_branches', action='store_true', help="Delete all the local patch/BASE/* branches, not just the ones that are being recreated.") - parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.") - parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.") - parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.") - args = parser.parse_args() - main() - -# vim: sw=4 et ft=python diff --git a/packaging/patch-update b/packaging/patch-update deleted file mode 100755 index fd56a9d8c..000000000 --- a/packaging/patch-update +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env -S python3 -B - -# This script is used to turn one or more of the "patch/BASE/*" branches -# into one or more diffs in the "patches" directory. Pass the option -# --gen if you want generated files in the diffs. Pass the name of -# one or more diffs if you want to just update a subset of all the -# diffs. - -import os, sys, re, argparse, time, shutil - -sys.path = ['packaging'] + sys.path - -from pkglib import * - -MAKE_GEN_CMDS = [ - './prepare-source'.split(), - 'cd build && if test -f config.status ; then ./config.status ; else ../configure ; fi', - 'make -C build gen'.split(), - ] -TMP_DIR = "patches.gen" - -os.environ['GIT_MERGE_AUTOEDIT'] = 'no' - -def main(): - global master_commit, parent_patch, description, completed, last_touch - - if not os.path.isdir(args.patches_dir): - die(f'No "{args.patches_dir}" directory was found.') - if not os.path.isdir('.git'): - die('No ".git" directory present in the current dir.') - - starting_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir) - - master_commit = latest_git_hash(args.base_branch) - - if cmd_txt_chk(['packaging/prep-auto-dir']).out == '': - die('You must setup an auto-build-save dir to use this script.') - - if args.gen: - if os.path.lexists(TMP_DIR): - die(f'"{TMP_DIR}" must not exist in the current directory.') - gen_files = get_gen_files() - os.mkdir(TMP_DIR, 0o700) - for cmd in MAKE_GEN_CMDS: - cmd_chk(cmd) - cmd_chk(['rsync', '-a', *gen_files, f'{TMP_DIR}/master/']) - - last_touch = int(time.time()) - - # Start by finding all patches so that we can load all possible parents. - patches = sorted(list(get_patch_branches(args.base_branch))) - - parent_patch = { } - description = { } - - for patch in patches: - branch = f"patch/{args.base_branch}/{patch}" - desc = '' - proc = cmd_pipe(['git', 'diff', '-U1000', f"{args.base_branch}...{branch}", '--', f"PATCH.{patch}"]) - in_diff = False - for line in proc.stdout: - if in_diff: - if not re.match(r'^[ +]', line): - continue - line = line[1:] - m = re.search(r'patch -p1 = int(time.time()): - time.sleep(1) - cmd_chk(['git', 'checkout', starting_branch]) - cmd_chk(['packaging/prep-auto-dir'], discard='output') - - -def update_patch(patch): - global last_touch - - completed.add(patch) # Mark it as completed early to short-circuit any (bogus) dependency loops. - - parent = parent_patch.get(patch, None) - if parent: - if parent not in completed: - if not update_patch(parent): - return 0 - based_on = parent = f"patch/{args.base_branch}/{parent}" - else: - parent = args.base_branch - based_on = master_commit - - print(f"======== {patch} ========") - - while args.gen and last_touch >= int(time.time()): - time.sleep(1) - - branch = f"patch/{args.base_branch}/{patch}" - s = cmd_run(['git', 'checkout', branch]) - if s.returncode != 0: - return 0 - - s = cmd_run(['git', 'merge', based_on]) - ok = s.returncode == 0 - skip_shell = False - if not ok or args.cmd or args.make or args.shell: - cmd_chk(['packaging/prep-auto-dir'], discard='output') - if not ok: - print(f'"git merge {based_on}" incomplete -- please fix.') - if not run_a_shell(parent, patch): - return 0 - if not args.make and not args.cmd: - skip_shell = True - if args.make: - if cmd_run(['packaging/smart-make']).returncode != 0: - if not run_a_shell(parent, patch): - return 0 - if not args.cmd: - skip_shell = True - if args.cmd: - if cmd_run(args.cmd).returncode != 0: - if not run_a_shell(parent, patch): - return 0 - skip_shell = True - if args.shell and not skip_shell: - if not run_a_shell(parent, patch): - return 0 - - with open(f"{args.patches_dir}/{patch}.diff", 'w', encoding='utf-8') as fh: - fh.write(description[patch]) - fh.write(f"\nbased-on: {based_on}\n") - - if args.gen: - gen_files = get_gen_files() - for cmd in MAKE_GEN_CMDS: - cmd_chk(cmd) - cmd_chk(['rsync', '-a', *gen_files, f"{TMP_DIR}/{patch}/"]) - else: - gen_files = [ ] - last_touch = int(time.time()) - - proc = cmd_pipe(['git', 'diff', based_on]) - skipping = False - for line in proc.stdout: - if skipping: - if not re.match(r'^diff --git a/', line): - continue - skipping = False - elif re.match(r'^diff --git a/PATCH', line): - skipping = True - continue - if not re.match(r'^index ', line): - fh.write(line) - proc.communicate() - - if args.gen: - e_tmp_dir = re.escape(TMP_DIR) - diff_re = re.compile(r'^(diff -Nurp) %s/[^/]+/(.*?) %s/[^/]+/(.*)' % (e_tmp_dir, e_tmp_dir)) - minus_re = re.compile(r'^\-\-\- %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir) - plus_re = re.compile(r'^\+\+\+ %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir) - - if parent == args.base_branch: - parent_dir = 'master' - else: - m = re.search(r'([^/]+)$', parent) - parent_dir = m[1] - - proc = cmd_pipe(['diff', '-Nurp', f"{TMP_DIR}/{parent_dir}", f"{TMP_DIR}/{patch}"]) - for line in proc.stdout: - line = diff_re.sub(r'\1 a/\2 b/\3', line) - line = minus_re.sub(r'--- a/\1', line) - line = plus_re.sub(r'+++ b/\1', line) - fh.write(line) - proc.communicate() - - return 1 - - -def run_a_shell(parent, patch): - m = re.search(r'([^/]+)$', parent) - parent_dir = m[1] - os.environ['PS1'] = f"[{parent_dir}] {patch}: " - - while True: - s = cmd_run([os.environ.get('SHELL', '/bin/sh')]) - if s.returncode != 0: - ans = input("Abort? [n/y] ") - if re.match(r'^y', ans, flags=re.I): - return False - continue - cur_branch, is_clean, status_txt = check_git_status(0) - if is_clean: - break - print(status_txt, end='') - - cmd_run('rm -f build/*.o build/*/*.o') - - return True - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Turn a git branch back into a diff files in the patches dir.", add_help=False) - parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.") - parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.") - parser.add_argument('--make', '-m', action='store_true', help="Run the smart-make script in every patch branch.") - parser.add_argument('--cmd', '-c', help="Run a command in every patch branch.") - parser.add_argument('--shell', '-s', action='store_true', help="Launch a shell for every patch/BASE/* branch updated, not just when a conflict occurs.") - parser.add_argument('--gen', metavar='DIR', nargs='?', const='', help='Include generated files. Optional DIR value overrides the default of using the "patches" dir.') - parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.") - parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.") - parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.") - args = parser.parse_args() - if args.gen == '': - args.gen = args.patches_dir - elif args.gen is not None: - args.patches_dir = args.gen - main() - -# vim: sw=4 et ft=python diff --git a/packaging/release-rsync b/packaging/release-rsync deleted file mode 100755 index 3abecc230..000000000 --- a/packaging/release-rsync +++ /dev/null @@ -1,385 +0,0 @@ -#!/usr/bin/env -S python3 -B - -# This script expects the directory ~/samba-rsync-ftp to exist and to be a -# copy of the /home/ftp/pub/rsync dir on samba.org. When the script is done, -# the git repository in the current directory will be updated, and the local -# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org. See the -# script samba-rsync for an easy way to initialize the local ftp copy and to -# thereafter update the remote files from your local copy. - -# This script also expects to be able to gpg sign the resulting tar files -# using your default gpg key. Make sure that the html download.html file -# has a link to the relevant keys that are authorized to sign the tar files -# and also make sure that the following commands work as expected: -# -# touch TeMp -# gpg --sign TeMp -# gpg --verify TeMp.gpg -# gpg --sign TeMp -# rm TeMp* -# -# The second time you sign the file it should NOT prompt you for your password -# (unless the timeout period has passed). It will prompt about overriding the -# existing TeMp.gpg file, though. - -import os, sys, re, argparse, glob, shutil, signal -from datetime import datetime -from getpass import getpass - -sys.path = ['packaging'] + sys.path - -from pkglib import * - -os.environ['LESS'] = 'mqeiXR'; # Make sure that -F is turned off and -R is turned on. -dest = os.environ['HOME'] + '/samba-rsync-ftp' -ORIGINAL_PATH = os.environ['PATH'] - -def main(): - if not os.path.isfile('packaging/release-rsync'): - die('You must run this script from the top of your rsync checkout.') - - now = datetime.now().astimezone() # Requires python 3.6 or later - cl_today = now.strftime('* %a %b %d %Y') - year = now.strftime('%Y') - ztoday = now.strftime('%d %b %Y') - today = ztoday.lstrip('0') - - # The MAINTAINER_TZ_OFFSET is a float number of hours vs UTC. It can start with '-' but not '+'. - tz_now = now.strftime('%z') - tz_num = tz_now[0:1].replace('+', '') + str(float(tz_now[1:3]) + float(tz_now[3:]) / 60) - - curdir = os.getcwd() - - signal.signal(signal.SIGINT, signal_handler) - - if cmd_txt_chk(['packaging/prep-auto-dir']).out == '': - die('You must setup an auto-build-save dir to use this script.'); - - auto_dir, gen_files = get_gen_files(True) - gen_pathnames = [ os.path.join(auto_dir, fn) for fn in gen_files ] - - dash_line = '=' * 74 - - print(f"""\ -{dash_line} -== This will release a new version of rsync onto an unsuspecting world. == -{dash_line} -""") - - with open('build/rsync.1') as fh: - for line in fh: - if line.startswith(r'.\" prefix='): - doc_prefix = line.split('=')[1].strip() - if doc_prefix != '/usr': - warn(f"*** The documentation was built with prefix {doc_prefix} instead of /usr ***") - die("*** Read the md2man script for a way to override this. ***") - break - if line.startswith('.P'): - die("Failed to find the prefix comment at the start of the rsync.1 manpage.") - - if not os.path.isdir(dest): - die(dest, "dest does not exist") - if not os.path.isdir('.git'): - die("There is no .git dir in the current directory.") - if os.path.lexists('a'): - die('"a" must not exist in the current directory.') - if os.path.lexists('b'): - die('"b" must not exist in the current directory.') - - curversion = get_rsync_version() - - # All version values are strings! - lastversion, last_protocol_version, pdate = get_NEWS_version_info() - protocol_version, subprotocol_version = get_protocol_versions() - - version = curversion - m = re.search(r'pre(\d+)', version) - if m: - version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version) - else: - version = version.replace('dev', 'pre1') - - ans = input(f"Please enter the version number of this release: [{version}] ") - if ans == '.': - version = re.sub(r'pre\d+', '', version) - elif ans != '': - version = ans - if not re.match(r'^[\d.]+(pre\d+)?$', version): - die(f'Invalid version: "{version}"') - - v_ver = 'v' + version - rsync_ver = 'rsync-' + version - - if os.path.lexists(rsync_ver): - die(f'"{rsync_ver}" must not exist in the current directory.') - - out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out - if out != '': - print(f"Tag {v_ver} already exists.") - ans = input("\nDelete tag or quit? [Q/del] ") - if not re.match(r'^del', ans, flags=re.I): - die("Aborted") - cmd_chk(['git', 'tag', '-d', v_ver]) - - version = re.sub(r'[-.]*pre[-.]*', 'pre', version) - if 'pre' in version and not curversion.endswith('dev'): - lastversion = curversion - - ans = input(f"Enter the previous version to produce a patch against: [{lastversion}] ") - if ans != '': - lastversion = ans - lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion) - - rsync_lastver = 'rsync-' + lastversion - if os.path.lexists(rsync_lastver): - die(f'"{rsync_lastver}" must not exist in the current directory.') - - m = re.search(r'(pre\d+)', version) - pre = m[1] if m else '' - - release = '0.1' if pre else '1' - ans = input(f"Please enter the RPM release number of this release: [{release}] ") - if ans != '': - release = ans - if pre: - release += '.' + pre - - finalversion = re.sub(r'pre\d+', '', version) - proto_changed = protocol_version != last_protocol_version - if proto_changed: - if finalversion in pdate: - proto_change_date = pdate[finalversion] - else: - while True: - ans = input("On what date did the protocol change to {protocol_version} get checked in? (dd Mmm yyyy) ") - if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans): - break - proto_change_date = ans - else: - proto_change_date = ' ' * 11 - - if 'pre' in lastversion: - if not pre: - die("You should not diff a release version against a pre-release version.") - srcdir = srcdiffdir = lastsrcdir = 'src-previews' - skipping = ' ** SKIPPING **' - elif pre: - srcdir = srcdiffdir = 'src-previews' - lastsrcdir = 'src' - skipping = ' ** SKIPPING **' - else: - srcdir = lastsrcdir = 'src' - srcdiffdir = 'src-diffs' - skipping = '' - - print(f""" -{dash_line} -version is "{version}" -lastversion is "{lastversion}" -dest is "{dest}" -curdir is "{curdir}" -srcdir is "{srcdir}" -srcdiffdir is "{srcdiffdir}" -lastsrcdir is "{lastsrcdir}" -release is "{release}" - -About to: - - tweak SUBPROTOCOL_VERSION in rsync.h, if needed - - tweak the version in version.h and the spec files - - tweak NEWS.md to ensure header values are correct - - generate configure.sh, config.h.in, and proto.h - - page through the differences -""") - ans = input(" ") - - specvars = { - 'Version:': finalversion, - 'Release:': release, - '%define fullversion': f'%{{version}}{pre}', - 'Released': version + '.', - '%define srcdir': srcdir, - } - - tweak_files = 'version.h rsync.h'.split() - tweak_files += glob.glob('packaging/*.spec') - tweak_files += glob.glob('packaging/*/*.spec') - - for fn in tweak_files: - with open(fn, 'r', encoding='utf-8') as fh: - old_txt = txt = fh.read() - if fn == 'version.h': - x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M) - msg = f"Unable to update RSYNC_VERSION in {fn}" - txt = replace_or_die(x_re, r'\1 "%s"' % version, txt, msg) - x_re = re.compile(r'^(#define MAINTAINER_TZ_OFFSET).*', re.M) - msg = f"Unable to update MAINTAINER_TZ_OFFSET in {fn}" - txt = replace_or_die(x_re, r'\1 ' + tz_num, txt, msg) - elif '.spec' in fn: - for var, val in specvars.items(): - x_re = re.compile(r'^%s .*' % re.escape(var), re.M) - txt = replace_or_die(x_re, var + ' ' + val, txt, f"Unable to update {var} in {fn}") - x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M) - txt = replace_or_die(x_re, r'%s \1' % cl_today, txt, f"Unable to update ChangeLog header in {fn}") - elif fn == 'rsync.h': - x_re = re.compile(r'(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)') - repl = lambda m: m[1] + ' ' + ('0' if not pre or not proto_changed else '1' if m[2] == '0' else m[2]) - txt = replace_or_die(x_re, repl, txt, f"Unable to find SUBPROTOCOL_VERSION define in {fn}") - elif fn == 'NEWS.md': - efv = re.escape(finalversion) - x_re = re.compile(r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv - + r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?') - rel_day = 'UNRELEASED' if pre else today - repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n' - + '## Changes in this version:\n') - if proto_changed: - repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n' - good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1) - msg = f"The top lines of {fn} are not in the right format. It should be:\n" + good_top - txt = replace_or_die(x_re, repl, txt, msg) - x_re = re.compile(r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv, re.M) - repl = lambda m: m[1] + (m[2] if pre else ztoday) + m[3] + proto_change_date + m[4] + protocol_version + m[5] - txt = replace_or_die(x_re, repl, txt, f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}') - else: - die(f"Unrecognized file in tweak_files: {fn}") - - if txt != old_txt: - print(f"Updating {fn}") - with open(fn, 'w', encoding='utf-8') as fh: - fh.write(txt) - - cmd_chk(['packaging/year-tweak']) - - print(dash_line) - cmd_run("git diff".split()) - - srctar_name = f"{rsync_ver}.tar.gz" - diff_name = f"{rsync_lastver}-{version}.diffs.gz" - srctar_file = os.path.join(dest, srcdir, srctar_name) - pattar_file = os.path.join(dest, srcdir, pattar_name) - diff_file = os.path.join(dest, srcdiffdir, diff_name) - lasttar_file = os.path.join(dest, lastsrcdir, rsync_lastver + '.tar.gz') - - print(f"""\ -{dash_line} - -About to: - - git commit all changes - - run a full build, ensuring that the manpages & configure.sh are up-to-date - - merge the {args.master_branch} branch into the patch/{args.master_branch}/* branches -""") - ans = input(" ") - - s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]']) - if s.returncode: - die('Aborting') - - cmd_chk('touch configure.ac && packaging/smart-make && make gen') - - print('Creating any missing patch branches.') - s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing') - if s.returncode: - die('Aborting') - - if re.match(r'^y', ans, re.I): - print(f'\nRunning smart-make on all "patch/{args.master_branch}/*" branches ...') - cmd_run(f"packaging/patch-update --branch={args.master_branch} --skip-check --make") - - print(f"""\ -{dash_line} - -About to: - - create signed tag for this release: {v_ver} - - create release diffs, "{diff_name}" - - create release tar, "{srctar_name}" - - update top-level README.md, NEWS.md, TODO, and ChangeLog - - update top-level rsync*.html manpages - - gpg-sign the release files - - update hard-linked top-level release files{skipping} -""") - ans = input(" ") - - # TODO: is there a better way to ensure that our passphrase is in the agent? - cmd_run("touch TeMp; gpg --sign TeMp; rm TeMp*") - - out = cmd_txt(f"git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out - print(out, end='') - if 'bad passphrase' in out or 'failed' in out: - die('Aborting') - - os.environ['PATH'] = ORIGINAL_PATH - - # Extract the generated files from the old tar. - tweaked_gen_files = [ os.path.join(rsync_lastver, fn) for fn in gen_files ] - cmd_run(['tar', 'xzf', lasttar_file, *tweaked_gen_files]) - os.rename(rsync_lastver, 'a') - - print(f"Creating {diff_file} ...") - cmd_chk(['rsync', '-a', *gen_pathnames, 'b/']) - - sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # CAUTION: must not contain any single quotes! - cmd_chk(f"(git diff v{lastversion} {v_ver} -- ':!.github'; diff -upN a b | sed -r '{sed_script}') | gzip -9 >{diff_file}") - shutil.rmtree('a') - os.rename('b', rsync_ver) - - print(f"Creating {srctar_file} ...") - cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | tar xf -") - cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver}/") - cmd_chk(['fakeroot', 'tar', 'czf', srctar_file, '--exclude=.github', rsync_ver]) - shutil.rmtree(rsync_ver) - - print(f"Updating the other files in {dest} ...") - md_files = 'README.md NEWS.md INSTALL.md'.split() - html_files = [ fn for fn in gen_pathnames if fn.endswith('.html') ] - cmd_chk(['rsync', '-a', *md_files, *html_files, dest]) - cmd_chk(["./md-convert", "--dest", dest, *md_files]) - - cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz") - - for fn in (srctar_file, pattar_file, diff_file): - asc_fn = fn + '.asc' - if os.path.lexists(asc_fn): - os.unlink(asc_fn) - res = cmd_run(['gpg', '--batch', '-ba', fn]) - if res.returncode != 0 and res.returncode != 2: - die("gpg signing failed") - - if not pre: - for find in f'{dest}/rsync-*.gz {dest}/rsync-*.asc {dest}/src-previews/rsync-*diffs.gz*'.split(): - for fn in glob.glob(find): - os.unlink(fn) - top_link = [ - srctar_file, f"{srctar_file}.asc", - pattar_file, f"{pattar_file}.asc", - diff_file, f"{diff_file}.asc", - ] - for fn in top_link: - os.link(fn, re.sub(r'/src(-\w+)?/', '/', fn)) - - print(f"""\ -{dash_line} - -Local changes are done. When you're satisfied, push the git repository -and rsync the release files. Remember to announce the release on *BOTH* -rsync-announce@lists.samba.org and rsync@lists.samba.org (and the web)! -""") - - -def replace_or_die(regex, repl, txt, die_msg): - m = regex.search(txt) - if not m: - die(die_msg) - return regex.sub(repl, txt, 1) - - -def signal_handler(sig, frame): - die("\nAborting due to SIGINT.") - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Prepare a new release of rsync in the git repo & ftp dir.", add_help=False) - parser.add_argument('--branch', '-b', dest='master_branch', default='master', help="The branch to release. Default: master.") - parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.") - args = parser.parse_args() - main() - -# vim: sw=4 et ft=python -- 2.47.3