diff options
-rw-r--r-- | project.py | 32 | ||||
-rw-r--r-- | subcmds/upload.py | 6 | ||||
-rw-r--r-- | tests/test_project.py | 10 |
3 files changed, 48 insertions, 0 deletions
@@ -21,6 +21,7 @@ import random | |||
21 | import re | 21 | import re |
22 | import shutil | 22 | import shutil |
23 | import stat | 23 | import stat |
24 | import string | ||
24 | import subprocess | 25 | import subprocess |
25 | import sys | 26 | import sys |
26 | import tarfile | 27 | import tarfile |
@@ -266,6 +267,7 @@ class ReviewableBranch: | |||
266 | dest_branch=None, | 267 | dest_branch=None, |
267 | validate_certs=True, | 268 | validate_certs=True, |
268 | push_options=None, | 269 | push_options=None, |
270 | patchset_description=None, | ||
269 | ): | 271 | ): |
270 | self.project.UploadForReview( | 272 | self.project.UploadForReview( |
271 | branch=self.name, | 273 | branch=self.name, |
@@ -281,6 +283,7 @@ class ReviewableBranch: | |||
281 | dest_branch=dest_branch, | 283 | dest_branch=dest_branch, |
282 | validate_certs=validate_certs, | 284 | validate_certs=validate_certs, |
283 | push_options=push_options, | 285 | push_options=push_options, |
286 | patchset_description=patchset_description, | ||
284 | ) | 287 | ) |
285 | 288 | ||
286 | def GetPublishedRefs(self): | 289 | def GetPublishedRefs(self): |
@@ -1089,6 +1092,7 @@ class Project: | |||
1089 | dest_branch=None, | 1092 | dest_branch=None, |
1090 | validate_certs=True, | 1093 | validate_certs=True, |
1091 | push_options=None, | 1094 | push_options=None, |
1095 | patchset_description=None, | ||
1092 | ): | 1096 | ): |
1093 | """Uploads the named branch for code review.""" | 1097 | """Uploads the named branch for code review.""" |
1094 | if branch is None: | 1098 | if branch is None: |
@@ -1171,6 +1175,10 @@ class Project: | |||
1171 | opts += ["wip"] | 1175 | opts += ["wip"] |
1172 | if ready: | 1176 | if ready: |
1173 | opts += ["ready"] | 1177 | opts += ["ready"] |
1178 | if patchset_description: | ||
1179 | opts += [ | ||
1180 | f"m={self._encode_patchset_description(patchset_description)}" | ||
1181 | ] | ||
1174 | if opts: | 1182 | if opts: |
1175 | ref_spec = ref_spec + "%" + ",".join(opts) | 1183 | ref_spec = ref_spec + "%" + ",".join(opts) |
1176 | cmd.append(ref_spec) | 1184 | cmd.append(ref_spec) |
@@ -1183,6 +1191,30 @@ class Project: | |||
1183 | R_PUB + branch.name, R_HEADS + branch.name, message=msg | 1191 | R_PUB + branch.name, R_HEADS + branch.name, message=msg |
1184 | ) | 1192 | ) |
1185 | 1193 | ||
1194 | @staticmethod | ||
1195 | def _encode_patchset_description(original): | ||
1196 | """Applies percent-encoding for strings sent as patchset description. | ||
1197 | |||
1198 | The encoding used is based on but stricter than URL encoding (Section | ||
1199 | 2.1 of RFC 3986). The only non-escaped characters are alphanumerics, and | ||
1200 | 'SPACE' (U+0020) can be represented as 'LOW LINE' (U+005F) or | ||
1201 | 'PLUS SIGN' (U+002B). | ||
1202 | |||
1203 | For more information, see the Gerrit docs here: | ||
1204 | https://gerrit-review.googlesource.com/Documentation/user-upload.html#patch_set_description | ||
1205 | """ | ||
1206 | SAFE = {ord(x) for x in string.ascii_letters + string.digits} | ||
1207 | |||
1208 | def _enc(b): | ||
1209 | if b in SAFE: | ||
1210 | return chr(b) | ||
1211 | elif b == ord(" "): | ||
1212 | return "_" | ||
1213 | else: | ||
1214 | return f"%{b:02x}" | ||
1215 | |||
1216 | return "".join(_enc(x) for x in original.encode("utf-8")) | ||
1217 | |||
1186 | def _ExtractArchive(self, tarpath, path=None): | 1218 | def _ExtractArchive(self, tarpath, path=None): |
1187 | """Extract the given tar on its current location | 1219 | """Extract the given tar on its current location |
1188 | 1220 | ||
diff --git a/subcmds/upload.py b/subcmds/upload.py index 4bcdfaf9..001453fe 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
@@ -245,6 +245,11 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
245 | help="add a label when uploading", | 245 | help="add a label when uploading", |
246 | ) | 246 | ) |
247 | p.add_option( | 247 | p.add_option( |
248 | "--pd", | ||
249 | "--patchset-description", | ||
250 | help="description for patchset", | ||
251 | ) | ||
252 | p.add_option( | ||
248 | "--re", | 253 | "--re", |
249 | "--reviewers", | 254 | "--reviewers", |
250 | type="string", | 255 | type="string", |
@@ -655,6 +660,7 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
655 | dest_branch=destination, | 660 | dest_branch=destination, |
656 | validate_certs=opt.validate_certs, | 661 | validate_certs=opt.validate_certs, |
657 | push_options=opt.push_options, | 662 | push_options=opt.push_options, |
663 | patchset_description=opt.patchset_description, | ||
658 | ) | 664 | ) |
659 | 665 | ||
660 | branch.uploaded = True | 666 | branch.uploaded = True |
diff --git a/tests/test_project.py b/tests/test_project.py index 6dc071bd..de26e91a 100644 --- a/tests/test_project.py +++ b/tests/test_project.py | |||
@@ -107,6 +107,16 @@ class ReviewableBranchTests(unittest.TestCase): | |||
107 | self.assertTrue(rb.date) | 107 | self.assertTrue(rb.date) |
108 | 108 | ||
109 | 109 | ||
110 | class ProjectTests(unittest.TestCase): | ||
111 | """Check Project behavior.""" | ||
112 | |||
113 | def test_encode_patchset_description(self): | ||
114 | self.assertEqual( | ||
115 | project.Project._encode_patchset_description("abcd00!! +"), | ||
116 | "abcd00%21%21_%2b", | ||
117 | ) | ||
118 | |||
119 | |||
110 | class CopyLinkTestCase(unittest.TestCase): | 120 | class CopyLinkTestCase(unittest.TestCase): |
111 | """TestCase for stub repo client checkouts. | 121 | """TestCase for stub repo client checkouts. |
112 | 122 | ||