Joshua Brindle | 13cd4c8 | 2008-08-19 15:30:36 -0400 | [diff] [blame] | 1 | #! /usr/bin/python |
| 2 | |
| 3 | # Basic instructions |
| 4 | # |
| 5 | # 1. Save patch email to file [patch.email] |
| 6 | # |
| 7 | # 2. Go to the svn directory to which you want to apply the patch. |
| 8 | # |
| 9 | # 3. Run "selinux-maint split patch.email". This will run vi on the |
| 10 | # logmsg (pulled out of the email) to allow you to add anything (ack |
| 11 | # messages). When you quit vi the current directory will have files |
| 12 | # called "patch" and "logmsg". |
| 13 | # |
| 14 | # 4. Run "selinux-maint apply" (optionally with a strip level as |
| 15 | # the last argument). This will do a dry run of applying the patch |
| 16 | # showing the results and ask if you want to apply the patch. If you |
| 17 | # say yes it will apply the patch and attempt to detect file adds (by |
| 18 | # comparing svn status and the output of patch). If it finds adds it |
| 19 | # will ask if you want to add each file. |
| 20 | # |
| 21 | # 5. Run "selinux-maint commit" to commit that patch with the log |
| 22 | # message. |
| 23 | # |
| 24 | # 6. Repeat 4 and 5 as often as necessary for a set of patch emails. |
| 25 | # |
| 26 | # 7. Run "selinux-maint rev packagename" where packagename is |
| 27 | # something like "libsepol". This will prompt for the new version |
| 28 | # number (showing the current), update VERSION, add a Changelog entry |
| 29 | # with the version and date, and vi the changelog for you to add |
| 30 | # entries. |
| 31 | # |
| 32 | # 8. Run "selinux-maint commit" again to commit the revision change |
| 33 | # (rev adds a simple log message - I just fixed this as my last |
| 34 | # checkin had the wrong log message). |
| 35 | |
| 36 | import sys |
| 37 | import subprocess |
| 38 | import shutil |
| 39 | import os |
| 40 | import os.path |
| 41 | import datetime |
| 42 | |
| 43 | dir = "/tmp/selinux-maint/" |
| 44 | |
| 45 | def usage(): |
| 46 | print "selinux-maint [command] [options]" |
| 47 | print "" |
| 48 | print "commands:" |
| 49 | print "\tsplit patch-email: split patch-email into a patch and log message" |
| 50 | print "\tapply [patch-level]: apply the patch and logmsg with optional level" |
| 51 | print "\tcommit username: commit the changes" |
| 52 | print "\trev package: update the version number and changelog of package" |
| 53 | print "\tmerge reva:revb source-branch: merge changes to the current branch" |
| 54 | |
| 55 | def create_tmpdir(): |
| 56 | try: |
| 57 | os.mkdir(dir) |
| 58 | except OSError: |
| 59 | if not os.path.isdir(dir): |
| 60 | print "path %s exists and is not a directory" % dir |
| 61 | sys.exit(1) |
| 62 | |
| 63 | def split_email(args): |
| 64 | # Get an absolute path for the patch email since we are going to |
| 65 | # change the working directory |
| 66 | patch_path = os.path.abspath(args[0]) |
| 67 | |
| 68 | create_tmpdir() |
| 69 | prevdir = os.getcwd() |
| 70 | os.chdir(dir) |
| 71 | |
| 72 | infd = open(patch_path) |
| 73 | outfd = open("info", "w") |
| 74 | retcode = subprocess.call(["git-mailinfo", "msg", "patch"], stdin=infd, |
| 75 | stdout=outfd) |
| 76 | if retcode != 0: |
| 77 | sys.exit(1) |
| 78 | |
| 79 | msgfd = open("logmsg", "w") |
| 80 | retcode = subprocess.call(["cat", "info", "msg"], stdout=msgfd) |
| 81 | |
| 82 | msgfd.close() |
| 83 | |
| 84 | retcode = subprocess.call(["vi", "logmsg"]) |
| 85 | |
| 86 | shutil.copyfile("logmsg", prevdir + "/logmsg") |
| 87 | shutil.copyfile("patch", prevdir + "/patch") |
| 88 | |
| 89 | def apply(args): |
| 90 | if len(args) >= 1: |
| 91 | patch_level = "-p%d" % int(args[0]) |
| 92 | else: |
| 93 | patch_level = "-p1" |
| 94 | |
| 95 | if len(args) == 2: |
| 96 | patch_name = "../patch" |
| 97 | patch_dir = args[1] |
| 98 | else: |
| 99 | patch_name = "patch" |
| 100 | patch_dir = None |
| 101 | |
| 102 | print "Test applying patch:" |
| 103 | if patch_dir: |
| 104 | os.chdir(patch_dir) |
| 105 | |
| 106 | patchfd = open(patch_name) |
| 107 | retcode = subprocess.call(["patch", patch_level, "--dry-run", "-l"], stdin=patchfd) |
| 108 | resp = raw_input("apply [y/n]: ") |
| 109 | if resp != "y": |
| 110 | sys.exit(0) |
| 111 | |
| 112 | patchfd = open(patch_name) |
| 113 | patch_output = subprocess.Popen(["patch", patch_level, "-l"], stdin=patchfd, |
| 114 | stdout=subprocess.PIPE).communicate()[0] |
| 115 | |
| 116 | status_output = subprocess.Popen(["svn", "status"], stdout=subprocess.PIPE).communicate()[0] |
| 117 | |
| 118 | |
| 119 | # Detect adds |
| 120 | unknown_files = [] |
| 121 | for status_line in status_output.split("\n"): |
| 122 | try: |
| 123 | status, fname = status_line.split() |
| 124 | except ValueError: |
| 125 | continue |
| 126 | if status == "?": |
| 127 | unknown_files.append(fname) |
| 128 | |
| 129 | added_files = [] |
| 130 | for patch_line in patch_output.split("\n"): |
| 131 | try: |
| 132 | patched_fname = patch_line.split(" ")[2] |
| 133 | except: |
| 134 | continue |
| 135 | if patched_fname in unknown_files: |
| 136 | added_files.append(patched_fname) |
| 137 | |
| 138 | for fname in added_files: |
| 139 | input = raw_input("add file %s [y/n]: " % fname) |
| 140 | if input == "y": |
| 141 | subprocess.call(["svn", "add", fname]) |
| 142 | |
| 143 | def commit(args): |
| 144 | if len(args) == 1: |
| 145 | retcode = subprocess.call(["svn", "commit", "--username", args[0], "-F", "logmsg"]) |
| 146 | else: |
| 147 | retcode = subprocess.call(["svn", "commit", "-F", "logmsg"]) |
| 148 | |
| 149 | |
| 150 | def rev(args): |
| 151 | if len(args) != 1: |
| 152 | print "you must provide a package name" |
| 153 | usage() |
| 154 | sys.exit(1) |
| 155 | package = args[0] |
| 156 | |
| 157 | ver_fd = open("%s/VERSION" % package, "r") |
| 158 | cur = ver_fd.read() |
| 159 | cur = cur.split("\n")[0] |
| 160 | ver_fd.close() |
| 161 | input = raw_input("new version [current is %s]: " % cur) |
| 162 | new_fd = open("%s/VERSION.new" % package, "w") |
| 163 | new_fd.write(input + "\n") |
| 164 | new_fd.close() |
| 165 | shutil.copyfile("%s/VERSION.new" % package, "%s/VERSION" % package) |
| 166 | |
| 167 | old_changelog = "%s/ChangeLog" % package |
| 168 | new_changelog = "%s/ChangeLog.new" % package |
| 169 | |
| 170 | n = open(new_changelog, "w") |
| 171 | |
| 172 | entry = "%s %s\n" % (input, str(datetime.date.today())) |
| 173 | n.write(entry) |
| 174 | n.write("\t*\n\n") |
| 175 | o = open(old_changelog) |
| 176 | n.write(o.read()) |
| 177 | n.close() |
| 178 | o.close() |
| 179 | |
| 180 | subprocess.call(["vi", new_changelog]) |
| 181 | shutil.copyfile(new_changelog, old_changelog) |
| 182 | |
| 183 | logmsg = open("logmsg", "w") |
| 184 | logmsg.write("updated %s to version %s\n" % (package, input)) |
| 185 | |
| 186 | def merge(args): |
| 187 | if len(args) != 2: |
| 188 | print "you must provide a revision pair and source branch" |
| 189 | usage() |
| 190 | sys.exit(1) |
| 191 | |
| 192 | rev = args[0] |
| 193 | branch = args[1] |
| 194 | |
| 195 | if branch == "trunk": |
| 196 | url = "https://selinux.svn.sourceforge.net/svnroot/selinux/trunk" |
| 197 | elif branch == "stable": |
| 198 | url = "https://selinux.svn.sourceforge.net/svnroot/selinux/branches/stable/1_0" |
| 199 | else: |
| 200 | url = "https://selinux.svn.sourceforge.net/svnroot/selinux/branches/%s" % branch |
| 201 | |
| 202 | subprocess.call(["svn", "diff", "-r%s" % rev, url]) |
| 203 | input = raw_input("apply these changes [y/n]? ") |
| 204 | if input != "y": |
| 205 | sys.exit(0) |
| 206 | |
| 207 | subprocess.call(["svn", "merge", "-r%s" % rev, url]) |
| 208 | |
| 209 | logmsg = open("logmsg", "w") |
| 210 | logmsg.write("applied r%s from %s\n" % (rev, branch)) |
| 211 | |
| 212 | |
| 213 | def main(): |
| 214 | if len(sys.argv) < 2: |
| 215 | usage() |
| 216 | sys.exit(1) |
| 217 | |
| 218 | command = sys.argv[1] |
| 219 | if command == "split": |
| 220 | split_email(sys.argv[2:]) |
| 221 | elif command == "apply": |
| 222 | apply(sys.argv[2:]) |
| 223 | elif command == "commit": |
| 224 | commit(sys.argv[2:]) |
| 225 | elif command == "rev": |
| 226 | rev(sys.argv[2:]) |
| 227 | elif command == "merge": |
| 228 | merge(sys.argv[2:]) |
| 229 | else: |
| 230 | usage() |
| 231 | |
| 232 | main() |