We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Pushing to master in deploy isn't working
As far as I can tell the deploy process is just completely failing to push to master every time and succeeding at pushing the tag every time. This suggests the problem is not github flakiness like I believed.

Notes:

* I could swear I've seen this working at least once, but maybe I'm misremembering.
* The command it's running *does* work locally
* It's running as me, so it should be allowed to push to master due to my admin privileges

I'm not really sure what's going on. I'll make a speculative pull request shortly to try and fix it, but it's hard to debug and I'm mostly just guessing at this point.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scripts/hypothesistooling.py`
Content:
```
1 # coding=utf-8
2 #
3 # This file is part of Hypothesis, which may be found at
4 # https://github.com/HypothesisWorks/hypothesis-python
5 #
6 # Most of this work is copyright (C) 2013-2017 David R. MacIver
7 # (david@drmaciver.com), but it contains contributions by others. See
8 # CONTRIBUTING.rst for a full list of people who may hold copyright, and
9 # consult the git log if you need to determine who owns an individual
10 # contribution.
11 #
12 # This Source Code Form is subject to the terms of the Mozilla Public License,
13 # v. 2.0. If a copy of the MPL was not distributed with this file, You can
14 # obtain one at http://mozilla.org/MPL/2.0/.
15 #
16 # END HEADER
17 
18 from __future__ import division, print_function, absolute_import
19 
20 import os
21 import re
22 import sys
23 import subprocess
24 from datetime import datetime, timedelta
25 
26 
27 def current_branch():
28     return subprocess.check_output([
29         'git', 'rev-parse', '--abbrev-ref', 'HEAD'
30     ]).decode('ascii').strip()
31 
32 
33 def tags():
34     result = [t.decode('ascii') for t in subprocess.check_output([
35         'git', 'tag'
36     ]).split(b"\n")]
37     assert len(set(result)) == len(result)
38     return set(result)
39 
40 
41 ROOT = subprocess.check_output([
42     'git', 'rev-parse', '--show-toplevel']).decode('ascii').strip()
43 SRC = os.path.join(ROOT, 'src')
44 
45 assert os.path.exists(SRC)
46 
47 
48 __version__ = None
49 __version_info__ = None
50 
51 VERSION_FILE = os.path.join(ROOT, 'src/hypothesis/version.py')
52 
53 with open(VERSION_FILE) as o:
54     exec(o.read())
55 
56 assert __version__ is not None
57 assert __version_info__ is not None
58 
59 
60 def latest_version():
61     versions = []
62 
63     for t in tags():
64         # All versions get tags but not all tags are versions (and there are
65         # a large number of historic tags with a different format for versions)
66         # so we parse each tag as a triple of ints (MAJOR, MINOR, PATCH)
67         # and skip any tag that doesn't match that.
68         assert t == t.strip()
69         parts = t.split('.')
70         if len(parts) != 3:
71             continue
72         try:
73             v = tuple(map(int, parts))
74         except ValueError:
75             continue
76 
77         versions.append((v, t))
78 
79     _, latest = max(versions)
80 
81     assert latest in tags()
82     return latest
83 
84 
85 def hash_for_name(name):
86     return subprocess.check_output([
87         'git', 'rev-parse', name
88     ]).decode('ascii').strip()
89 
90 
91 def is_ancestor(a, b):
92     check = subprocess.call([
93         'git', 'merge-base', '--is-ancestor', a, b
94     ])
95     assert 0 <= check <= 1
96     return check == 0
97 
98 
99 CHANGELOG_FILE = os.path.join(ROOT, 'docs', 'changes.rst')
100 
101 
102 def changelog():
103     with open(CHANGELOG_FILE) as i:
104         return i.read()
105 
106 
107 def merge_base(a, b):
108     return subprocess.check_output([
109         'git', 'merge-base', a, b,
110     ]).strip()
111 
112 
113 def has_source_changes(version=None):
114     if version is None:
115         version = latest_version()
116 
117     # Check where we branched off from the version. We're only interested
118     # in whether *we* introduced any source changes, so we check diff from
119     # there rather than the diff to the other side.
120     point_of_divergence = merge_base('HEAD', version)
121 
122     return subprocess.call([
123         'git', 'diff', '--exit-code', point_of_divergence, 'HEAD', '--', SRC,
124     ]) != 0
125 
126 
127 def has_uncommitted_changes(filename):
128     return subprocess.call([
129         'git', 'diff', '--exit-code', filename
130     ]) != 0
131 
132 
133 def git(*args):
134     subprocess.check_call(('git',) + args)
135 
136 
137 def create_tag_and_push():
138     assert __version__ not in tags()
139     git('config', 'user.name', 'Travis CI on behalf of David R. MacIver')
140     git('config', 'user.email', 'david@drmaciver.com')
141     git('config', 'core.sshCommand', 'ssh -i deploy_key')
142     git(
143         'remote', 'add', 'ssh-origin',
144         'git@github.com:HypothesisWorks/hypothesis-python.git'
145     )
146     git('tag', __version__)
147 
148     for command in [
149         'git push ssh-origin --tags',
150         'git push ssh-origin HEAD:master',
151     ]:
152         subprocess.check_call([
153             'ssh-agent', 'sh', '-c',
154             'chmod 0600 deploy_key && ' +
155             'ssh-add deploy_key && ' +
156             command
157         ])
158 
159 
160 def build_jobs():
161     """Query the Travis API to find out what the state of the other build jobs
162     is.
163 
164     Note: This usage of Travis has been somewhat reverse engineered due
165     to a certain dearth of documentation as to what values what takes
166     when.
167 
168     """
169     import requests
170 
171     build_id = os.environ['TRAVIS_BUILD_ID']
172 
173     url = 'https://api.travis-ci.org/builds/%s' % (build_id,)
174     data = requests.get(url, headers={
175         'Accept': 'application/vnd.travis-ci.2+json'
176     }).json()
177 
178     matrix = data['jobs']
179 
180     jobs = {}
181 
182     for m in matrix:
183         name = m['config']['env'].replace('TASK=', '')
184         status = m['state']
185         jobs.setdefault(status, []).append(name)
186     return jobs
187 
188 
189 def modified_files():
190     files = set()
191     for command in [
192         ['git', 'diff', '--name-only', '--diff-filter=d',
193             latest_version(), 'HEAD'],
194         ['git', 'diff', '--name-only']
195     ]:
196         diff_output = subprocess.check_output(command).decode('ascii')
197         for l in diff_output.split('\n'):
198             filepath = l.strip()
199             if filepath:
200                 assert os.path.exists(filepath)
201                 files.add(filepath)
202     return files
203 
204 
205 def all_files():
206     return subprocess.check_output(['git', 'ls-files']).decode(
207         'ascii').splitlines()
208 
209 
210 RELEASE_FILE = os.path.join(ROOT, 'RELEASE.rst')
211 
212 
213 def has_release():
214     return os.path.exists(RELEASE_FILE)
215 
216 
217 CHANGELOG_BORDER = re.compile(r"^-+$")
218 CHANGELOG_HEADER = re.compile(r"^\d+\.\d+\.\d+ - \d\d\d\d-\d\d-\d\d$")
219 RELEASE_TYPE = re.compile(r"^RELEASE_TYPE: +(major|minor|patch)")
220 
221 
222 MAJOR = 'major'
223 MINOR = 'minor'
224 PATCH = 'patch'
225 
226 VALID_RELEASE_TYPES = (MAJOR, MINOR, PATCH)
227 
228 
229 def parse_release_file():
230     with open(RELEASE_FILE) as i:
231         release_contents = i.read()
232 
233     release_lines = release_contents.split('\n')
234 
235     m = RELEASE_TYPE.match(release_lines[0])
236     if m is not None:
237         release_type = m.group(1)
238         if release_type not in VALID_RELEASE_TYPES:
239             print('Unrecognised release type %r' % (release_type,))
240             sys.exit(1)
241         del release_lines[0]
242         release_contents = '\n'.join(release_lines).strip()
243     else:
244         print(
245             'RELEASE.rst does not start by specifying release type. The first '
246             'line of the file should be RELEASE_TYPE: followed by one of '
247             'major, minor, or patch, to specify the type of release that '
248             'this is (i.e. which version number to increment). Instead the '
249             'first line was %r' % (release_lines[0],)
250         )
251         sys.exit(1)
252 
253     return release_type, release_contents
254 
255 
256 def update_changelog_and_version():
257     global __version_info__
258     global __version__
259 
260     with open(CHANGELOG_FILE) as i:
261         contents = i.read()
262     assert '\r' not in contents
263     lines = contents.split('\n')
264     assert contents == '\n'.join(lines)
265     for i, l in enumerate(lines):
266         if CHANGELOG_BORDER.match(l):
267             assert CHANGELOG_HEADER.match(lines[i + 1]), repr(lines[i + 1])
268             assert CHANGELOG_BORDER.match(lines[i + 2]), repr(lines[i + 2])
269             beginning = '\n'.join(lines[:i])
270             rest = '\n'.join(lines[i:])
271             assert '\n'.join((beginning, rest)) == contents
272             break
273 
274     release_type, release_contents = parse_release_file()
275 
276     new_version = list(__version_info__)
277     bump = VALID_RELEASE_TYPES.index(release_type)
278     new_version[bump] += 1
279     for i in range(bump + 1, len(new_version)):
280         new_version[i] = 0
281     new_version = tuple(new_version)
282     new_version_string = '.'.join(map(str, new_version))
283 
284     __version_info__ = new_version
285     __version__ = new_version_string
286 
287     with open(VERSION_FILE) as i:
288         version_lines = i.read().split('\n')
289 
290     for i, l in enumerate(version_lines):
291         if 'version_info' in l:
292             version_lines[i] = '__version_info__ = %r' % (new_version,)
293             break
294 
295     with open(VERSION_FILE, 'w') as o:
296         o.write('\n'.join(version_lines))
297 
298     now = datetime.utcnow()
299 
300     date = max([
301         d.strftime('%Y-%m-%d') for d in (now, now + timedelta(hours=1))
302     ])
303 
304     heading_for_new_version = ' - '.join((new_version_string, date))
305     border_for_new_version = '-' * len(heading_for_new_version)
306 
307     new_changelog_parts = [
308         beginning.strip(),
309         '',
310         border_for_new_version,
311         heading_for_new_version,
312         border_for_new_version,
313         '',
314         release_contents,
315         '',
316         rest
317     ]
318 
319     with open(CHANGELOG_FILE, 'w') as o:
320         o.write('\n'.join(new_changelog_parts))
321 
322 
323 def update_for_pending_release():
324     update_changelog_and_version()
325 
326     git('rm', RELEASE_FILE)
327     git('add', CHANGELOG_FILE, VERSION_FILE)
328 
329     git(
330         'commit',
331         '-m', 'Bump version to %s and update changelog' % (__version__,)
332     )
333
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scripts/hypothesistooling.py b/scripts/hypothesistooling.py
--- a/scripts/hypothesistooling.py
+++ b/scripts/hypothesistooling.py
@@ -145,16 +145,13 @@
     )
     git('tag', __version__)
 
-    for command in [
-        'git push ssh-origin --tags',
-        'git push ssh-origin HEAD:master',
-    ]:
-        subprocess.check_call([
-            'ssh-agent', 'sh', '-c',
-            'chmod 0600 deploy_key && ' +
-            'ssh-add deploy_key && ' +
-            command
-        ])
+    subprocess.check_call([
+        'ssh-agent', 'sh', '-c',
+        'chmod 0600 deploy_key && ' +
+        'ssh-add deploy_key && ' +
+        'git push ssh-origin HEAD:master &&'
+        'git push ssh-origin --tags'
+    ])
 
 
 def build_jobs():

{"golden_diff": "diff --git a/scripts/hypothesistooling.py b/scripts/hypothesistooling.py\n--- a/scripts/hypothesistooling.py\n+++ b/scripts/hypothesistooling.py\n@@ -145,16 +145,13 @@\n     )\n     git('tag', __version__)\n \n-    for command in [\n-        'git push ssh-origin --tags',\n-        'git push ssh-origin HEAD:master',\n-    ]:\n-        subprocess.check_call([\n-            'ssh-agent', 'sh', '-c',\n-            'chmod 0600 deploy_key && ' +\n-            'ssh-add deploy_key && ' +\n-            command\n-        ])\n+    subprocess.check_call([\n+        'ssh-agent', 'sh', '-c',\n+        'chmod 0600 deploy_key && ' +\n+        'ssh-add deploy_key && ' +\n+        'git push ssh-origin HEAD:master &&'\n+        'git push ssh-origin --tags'\n+    ])\n \n \n def build_jobs():\n", "issue": "Pushing to master in deploy isn't working\nAs far as I can tell the deploy process is just completely failing to push to master every time and succeeding at pushing the tag every time. This suggests the problem is not github flakiness like I believed.\r\n\r\nNotes:\r\n\r\n* I could swear I've seen this working at least once, but maybe I'm misremembering.\r\n* The command it's running *does* work locally\r\n* It's running as me, so it should be allowed to push to master due to my admin privileges\r\n\r\nI'm not really sure what's going on. I'll make a speculative pull request shortly to try and fix it, but it's hard to debug and I'm mostly just guessing at this point.\n", "before_files": [{"content": "# coding=utf-8\n#\n# This file is part of Hypothesis, which may be found at\n# https://github.com/HypothesisWorks/hypothesis-python\n#\n# Most of this work is copyright (C) 2013-2017 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# CONTRIBUTING.rst for a full list of people who may hold copyright, and\n# consult the git log if you need to determine who owns an individual\n# contribution.\n#\n# This Source Code Form is subject to the terms of the Mozilla Public License,\n# v. 2.0. If a copy of the MPL was not distributed with this file, You can\n# obtain one at http://mozilla.org/MPL/2.0/.\n#\n# END HEADER\n\nfrom __future__ import division, print_function, absolute_import\n\nimport os\nimport re\nimport sys\nimport subprocess\nfrom datetime import datetime, timedelta\n\n\ndef current_branch():\n    return subprocess.check_output([\n        'git', 'rev-parse', '--abbrev-ref', 'HEAD'\n    ]).decode('ascii').strip()\n\n\ndef tags():\n    result = [t.decode('ascii') for t in subprocess.check_output([\n        'git', 'tag'\n    ]).split(b\"\\n\")]\n    assert len(set(result)) == len(result)\n    return set(result)\n\n\nROOT = subprocess.check_output([\n    'git', 'rev-parse', '--show-toplevel']).decode('ascii').strip()\nSRC = os.path.join(ROOT, 'src')\n\nassert os.path.exists(SRC)\n\n\n__version__ = None\n__version_info__ = None\n\nVERSION_FILE = os.path.join(ROOT, 'src/hypothesis/version.py')\n\nwith open(VERSION_FILE) as o:\n    exec(o.read())\n\nassert __version__ is not None\nassert __version_info__ is not None\n\n\ndef latest_version():\n    versions = []\n\n    for t in tags():\n        # All versions get tags but not all tags are versions (and there are\n        # a large number of historic tags with a different format for versions)\n        # so we parse each tag as a triple of ints (MAJOR, MINOR, PATCH)\n        # and skip any tag that doesn't match that.\n        assert t == t.strip()\n        parts = t.split('.')\n        if len(parts) != 3:\n            continue\n        try:\n            v = tuple(map(int, parts))\n        except ValueError:\n            continue\n\n        versions.append((v, t))\n\n    _, latest = max(versions)\n\n    assert latest in tags()\n    return latest\n\n\ndef hash_for_name(name):\n    return subprocess.check_output([\n        'git', 'rev-parse', name\n    ]).decode('ascii').strip()\n\n\ndef is_ancestor(a, b):\n    check = subprocess.call([\n        'git', 'merge-base', '--is-ancestor', a, b\n    ])\n    assert 0 <= check <= 1\n    return check == 0\n\n\nCHANGELOG_FILE = os.path.join(ROOT, 'docs', 'changes.rst')\n\n\ndef changelog():\n    with open(CHANGELOG_FILE) as i:\n        return i.read()\n\n\ndef merge_base(a, b):\n    return subprocess.check_output([\n        'git', 'merge-base', a, b,\n    ]).strip()\n\n\ndef has_source_changes(version=None):\n    if version is None:\n        version = latest_version()\n\n    # Check where we branched off from the version. We're only interested\n    # in whether *we* introduced any source changes, so we check diff from\n    # there rather than the diff to the other side.\n    point_of_divergence = merge_base('HEAD', version)\n\n    return subprocess.call([\n        'git', 'diff', '--exit-code', point_of_divergence, 'HEAD', '--', SRC,\n    ]) != 0\n\n\ndef has_uncommitted_changes(filename):\n    return subprocess.call([\n        'git', 'diff', '--exit-code', filename\n    ]) != 0\n\n\ndef git(*args):\n    subprocess.check_call(('git',) + args)\n\n\ndef create_tag_and_push():\n    assert __version__ not in tags()\n    git('config', 'user.name', 'Travis CI on behalf of David R. MacIver')\n    git('config', 'user.email', 'david@drmaciver.com')\n    git('config', 'core.sshCommand', 'ssh -i deploy_key')\n    git(\n        'remote', 'add', 'ssh-origin',\n        'git@github.com:HypothesisWorks/hypothesis-python.git'\n    )\n    git('tag', __version__)\n\n    for command in [\n        'git push ssh-origin --tags',\n        'git push ssh-origin HEAD:master',\n    ]:\n        subprocess.check_call([\n            'ssh-agent', 'sh', '-c',\n            'chmod 0600 deploy_key && ' +\n            'ssh-add deploy_key && ' +\n            command\n        ])\n\n\ndef build_jobs():\n    \"\"\"Query the Travis API to find out what the state of the other build jobs\n    is.\n\n    Note: This usage of Travis has been somewhat reverse engineered due\n    to a certain dearth of documentation as to what values what takes\n    when.\n\n    \"\"\"\n    import requests\n\n    build_id = os.environ['TRAVIS_BUILD_ID']\n\n    url = 'https://api.travis-ci.org/builds/%s' % (build_id,)\n    data = requests.get(url, headers={\n        'Accept': 'application/vnd.travis-ci.2+json'\n    }).json()\n\n    matrix = data['jobs']\n\n    jobs = {}\n\n    for m in matrix:\n        name = m['config']['env'].replace('TASK=', '')\n        status = m['state']\n        jobs.setdefault(status, []).append(name)\n    return jobs\n\n\ndef modified_files():\n    files = set()\n    for command in [\n        ['git', 'diff', '--name-only', '--diff-filter=d',\n            latest_version(), 'HEAD'],\n        ['git', 'diff', '--name-only']\n    ]:\n        diff_output = subprocess.check_output(command).decode('ascii')\n        for l in diff_output.split('\\n'):\n            filepath = l.strip()\n            if filepath:\n                assert os.path.exists(filepath)\n                files.add(filepath)\n    return files\n\n\ndef all_files():\n    return subprocess.check_output(['git', 'ls-files']).decode(\n        'ascii').splitlines()\n\n\nRELEASE_FILE = os.path.join(ROOT, 'RELEASE.rst')\n\n\ndef has_release():\n    return os.path.exists(RELEASE_FILE)\n\n\nCHANGELOG_BORDER = re.compile(r\"^-+$\")\nCHANGELOG_HEADER = re.compile(r\"^\\d+\\.\\d+\\.\\d+ - \\d\\d\\d\\d-\\d\\d-\\d\\d$\")\nRELEASE_TYPE = re.compile(r\"^RELEASE_TYPE: +(major|minor|patch)\")\n\n\nMAJOR = 'major'\nMINOR = 'minor'\nPATCH = 'patch'\n\nVALID_RELEASE_TYPES = (MAJOR, MINOR, PATCH)\n\n\ndef parse_release_file():\n    with open(RELEASE_FILE) as i:\n        release_contents = i.read()\n\n    release_lines = release_contents.split('\\n')\n\n    m = RELEASE_TYPE.match(release_lines[0])\n    if m is not None:\n        release_type = m.group(1)\n        if release_type not in VALID_RELEASE_TYPES:\n            print('Unrecognised release type %r' % (release_type,))\n            sys.exit(1)\n        del release_lines[0]\n        release_contents = '\\n'.join(release_lines).strip()\n    else:\n        print(\n            'RELEASE.rst does not start by specifying release type. The first '\n            'line of the file should be RELEASE_TYPE: followed by one of '\n            'major, minor, or patch, to specify the type of release that '\n            'this is (i.e. which version number to increment). Instead the '\n            'first line was %r' % (release_lines[0],)\n        )\n        sys.exit(1)\n\n    return release_type, release_contents\n\n\ndef update_changelog_and_version():\n    global __version_info__\n    global __version__\n\n    with open(CHANGELOG_FILE) as i:\n        contents = i.read()\n    assert '\\r' not in contents\n    lines = contents.split('\\n')\n    assert contents == '\\n'.join(lines)\n    for i, l in enumerate(lines):\n        if CHANGELOG_BORDER.match(l):\n            assert CHANGELOG_HEADER.match(lines[i + 1]), repr(lines[i + 1])\n            assert CHANGELOG_BORDER.match(lines[i + 2]), repr(lines[i + 2])\n            beginning = '\\n'.join(lines[:i])\n            rest = '\\n'.join(lines[i:])\n            assert '\\n'.join((beginning, rest)) == contents\n            break\n\n    release_type, release_contents = parse_release_file()\n\n    new_version = list(__version_info__)\n    bump = VALID_RELEASE_TYPES.index(release_type)\n    new_version[bump] += 1\n    for i in range(bump + 1, len(new_version)):\n        new_version[i] = 0\n    new_version = tuple(new_version)\n    new_version_string = '.'.join(map(str, new_version))\n\n    __version_info__ = new_version\n    __version__ = new_version_string\n\n    with open(VERSION_FILE) as i:\n        version_lines = i.read().split('\\n')\n\n    for i, l in enumerate(version_lines):\n        if 'version_info' in l:\n            version_lines[i] = '__version_info__ = %r' % (new_version,)\n            break\n\n    with open(VERSION_FILE, 'w') as o:\n        o.write('\\n'.join(version_lines))\n\n    now = datetime.utcnow()\n\n    date = max([\n        d.strftime('%Y-%m-%d') for d in (now, now + timedelta(hours=1))\n    ])\n\n    heading_for_new_version = ' - '.join((new_version_string, date))\n    border_for_new_version = '-' * len(heading_for_new_version)\n\n    new_changelog_parts = [\n        beginning.strip(),\n        '',\n        border_for_new_version,\n        heading_for_new_version,\n        border_for_new_version,\n        '',\n        release_contents,\n        '',\n        rest\n    ]\n\n    with open(CHANGELOG_FILE, 'w') as o:\n        o.write('\\n'.join(new_changelog_parts))\n\n\ndef update_for_pending_release():\n    update_changelog_and_version()\n\n    git('rm', RELEASE_FILE)\n    git('add', CHANGELOG_FILE, VERSION_FILE)\n\n    git(\n        'commit',\n        '-m', 'Bump version to %s and update changelog' % (__version__,)\n    )\n", "path": "scripts/hypothesistooling.py"}], "after_files": [{"content": "# coding=utf-8\n#\n# This file is part of Hypothesis, which may be found at\n# https://github.com/HypothesisWorks/hypothesis-python\n#\n# Most of this work is copyright (C) 2013-2017 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# CONTRIBUTING.rst for a full list of people who may hold copyright, and\n# consult the git log if you need to determine who owns an individual\n# contribution.\n#\n# This Source Code Form is subject to the terms of the Mozilla Public License,\n# v. 2.0. If a copy of the MPL was not distributed with this file, You can\n# obtain one at http://mozilla.org/MPL/2.0/.\n#\n# END HEADER\n\nfrom __future__ import division, print_function, absolute_import\n\nimport os\nimport re\nimport sys\nimport subprocess\nfrom datetime import datetime, timedelta\n\n\ndef current_branch():\n    return subprocess.check_output([\n        'git', 'rev-parse', '--abbrev-ref', 'HEAD'\n    ]).decode('ascii').strip()\n\n\ndef tags():\n    result = [t.decode('ascii') for t in subprocess.check_output([\n        'git', 'tag'\n    ]).split(b\"\\n\")]\n    assert len(set(result)) == len(result)\n    return set(result)\n\n\nROOT = subprocess.check_output([\n    'git', 'rev-parse', '--show-toplevel']).decode('ascii').strip()\nSRC = os.path.join(ROOT, 'src')\n\nassert os.path.exists(SRC)\n\n\n__version__ = None\n__version_info__ = None\n\nVERSION_FILE = os.path.join(ROOT, 'src/hypothesis/version.py')\n\nwith open(VERSION_FILE) as o:\n    exec(o.read())\n\nassert __version__ is not None\nassert __version_info__ is not None\n\n\ndef latest_version():\n    versions = []\n\n    for t in tags():\n        # All versions get tags but not all tags are versions (and there are\n        # a large number of historic tags with a different format for versions)\n        # so we parse each tag as a triple of ints (MAJOR, MINOR, PATCH)\n        # and skip any tag that doesn't match that.\n        assert t == t.strip()\n        parts = t.split('.')\n        if len(parts) != 3:\n            continue\n        try:\n            v = tuple(map(int, parts))\n        except ValueError:\n            continue\n\n        versions.append((v, t))\n\n    _, latest = max(versions)\n\n    assert latest in tags()\n    return latest\n\n\ndef hash_for_name(name):\n    return subprocess.check_output([\n        'git', 'rev-parse', name\n    ]).decode('ascii').strip()\n\n\ndef is_ancestor(a, b):\n    check = subprocess.call([\n        'git', 'merge-base', '--is-ancestor', a, b\n    ])\n    assert 0 <= check <= 1\n    return check == 0\n\n\nCHANGELOG_FILE = os.path.join(ROOT, 'docs', 'changes.rst')\n\n\ndef changelog():\n    with open(CHANGELOG_FILE) as i:\n        return i.read()\n\n\ndef merge_base(a, b):\n    return subprocess.check_output([\n        'git', 'merge-base', a, b,\n    ]).strip()\n\n\ndef has_source_changes(version=None):\n    if version is None:\n        version = latest_version()\n\n    # Check where we branched off from the version. We're only interested\n    # in whether *we* introduced any source changes, so we check diff from\n    # there rather than the diff to the other side.\n    point_of_divergence = merge_base('HEAD', version)\n\n    return subprocess.call([\n        'git', 'diff', '--exit-code', point_of_divergence, 'HEAD', '--', SRC,\n    ]) != 0\n\n\ndef has_uncommitted_changes(filename):\n    return subprocess.call([\n        'git', 'diff', '--exit-code', filename\n    ]) != 0\n\n\ndef git(*args):\n    subprocess.check_call(('git',) + args)\n\n\ndef create_tag_and_push():\n    assert __version__ not in tags()\n    git('config', 'user.name', 'Travis CI on behalf of David R. MacIver')\n    git('config', 'user.email', 'david@drmaciver.com')\n    git('config', 'core.sshCommand', 'ssh -i deploy_key')\n    git(\n        'remote', 'add', 'ssh-origin',\n        'git@github.com:HypothesisWorks/hypothesis-python.git'\n    )\n    git('tag', __version__)\n\n    subprocess.check_call([\n        'ssh-agent', 'sh', '-c',\n        'chmod 0600 deploy_key && ' +\n        'ssh-add deploy_key && ' +\n        'git push ssh-origin HEAD:master &&'\n        'git push ssh-origin --tags'\n    ])\n\n\ndef build_jobs():\n    \"\"\"Query the Travis API to find out what the state of the other build jobs\n    is.\n\n    Note: This usage of Travis has been somewhat reverse engineered due\n    to a certain dearth of documentation as to what values what takes\n    when.\n\n    \"\"\"\n    import requests\n\n    build_id = os.environ['TRAVIS_BUILD_ID']\n\n    url = 'https://api.travis-ci.org/builds/%s' % (build_id,)\n    data = requests.get(url, headers={\n        'Accept': 'application/vnd.travis-ci.2+json'\n    }).json()\n\n    matrix = data['jobs']\n\n    jobs = {}\n\n    for m in matrix:\n        name = m['config']['env'].replace('TASK=', '')\n        status = m['state']\n        jobs.setdefault(status, []).append(name)\n    return jobs\n\n\ndef modified_files():\n    files = set()\n    for command in [\n        ['git', 'diff', '--name-only', '--diff-filter=d',\n            latest_version(), 'HEAD'],\n        ['git', 'diff', '--name-only']\n    ]:\n        diff_output = subprocess.check_output(command).decode('ascii')\n        for l in diff_output.split('\\n'):\n            filepath = l.strip()\n            if filepath:\n                assert os.path.exists(filepath)\n                files.add(filepath)\n    return files\n\n\ndef all_files():\n    return subprocess.check_output(['git', 'ls-files']).decode(\n        'ascii').splitlines()\n\n\nRELEASE_FILE = os.path.join(ROOT, 'RELEASE.rst')\n\n\ndef has_release():\n    return os.path.exists(RELEASE_FILE)\n\n\nCHANGELOG_BORDER = re.compile(r\"^-+$\")\nCHANGELOG_HEADER = re.compile(r\"^\\d+\\.\\d+\\.\\d+ - \\d\\d\\d\\d-\\d\\d-\\d\\d$\")\nRELEASE_TYPE = re.compile(r\"^RELEASE_TYPE: +(major|minor|patch)\")\n\n\nMAJOR = 'major'\nMINOR = 'minor'\nPATCH = 'patch'\n\nVALID_RELEASE_TYPES = (MAJOR, MINOR, PATCH)\n\n\ndef parse_release_file():\n    with open(RELEASE_FILE) as i:\n        release_contents = i.read()\n\n    release_lines = release_contents.split('\\n')\n\n    m = RELEASE_TYPE.match(release_lines[0])\n    if m is not None:\n        release_type = m.group(1)\n        if release_type not in VALID_RELEASE_TYPES:\n            print('Unrecognised release type %r' % (release_type,))\n            sys.exit(1)\n        del release_lines[0]\n        release_contents = '\\n'.join(release_lines).strip()\n    else:\n        print(\n            'RELEASE.rst does not start by specifying release type. The first '\n            'line of the file should be RELEASE_TYPE: followed by one of '\n            'major, minor, or patch, to specify the type of release that '\n            'this is (i.e. which version number to increment). Instead the '\n            'first line was %r' % (release_lines[0],)\n        )\n        sys.exit(1)\n\n    return release_type, release_contents\n\n\ndef update_changelog_and_version():\n    global __version_info__\n    global __version__\n\n    with open(CHANGELOG_FILE) as i:\n        contents = i.read()\n    assert '\\r' not in contents\n    lines = contents.split('\\n')\n    assert contents == '\\n'.join(lines)\n    for i, l in enumerate(lines):\n        if CHANGELOG_BORDER.match(l):\n            assert CHANGELOG_HEADER.match(lines[i + 1]), repr(lines[i + 1])\n            assert CHANGELOG_BORDER.match(lines[i + 2]), repr(lines[i + 2])\n            beginning = '\\n'.join(lines[:i])\n            rest = '\\n'.join(lines[i:])\n            assert '\\n'.join((beginning, rest)) == contents\n            break\n\n    release_type, release_contents = parse_release_file()\n\n    new_version = list(__version_info__)\n    bump = VALID_RELEASE_TYPES.index(release_type)\n    new_version[bump] += 1\n    for i in range(bump + 1, len(new_version)):\n        new_version[i] = 0\n    new_version = tuple(new_version)\n    new_version_string = '.'.join(map(str, new_version))\n\n    __version_info__ = new_version\n    __version__ = new_version_string\n\n    with open(VERSION_FILE) as i:\n        version_lines = i.read().split('\\n')\n\n    for i, l in enumerate(version_lines):\n        if 'version_info' in l:\n            version_lines[i] = '__version_info__ = %r' % (new_version,)\n            break\n\n    with open(VERSION_FILE, 'w') as o:\n        o.write('\\n'.join(version_lines))\n\n    now = datetime.utcnow()\n\n    date = max([\n        d.strftime('%Y-%m-%d') for d in (now, now + timedelta(hours=1))\n    ])\n\n    heading_for_new_version = ' - '.join((new_version_string, date))\n    border_for_new_version = '-' * len(heading_for_new_version)\n\n    new_changelog_parts = [\n        beginning.strip(),\n        '',\n        border_for_new_version,\n        heading_for_new_version,\n        border_for_new_version,\n        '',\n        release_contents,\n        '',\n        rest\n    ]\n\n    with open(CHANGELOG_FILE, 'w') as o:\n        o.write('\\n'.join(new_changelog_parts))\n\n\ndef update_for_pending_release():\n    update_changelog_and_version()\n\n    git('rm', RELEASE_FILE)\n    git('add', CHANGELOG_FILE, VERSION_FILE)\n\n    git(\n        'commit',\n        '-m', 'Bump version to %s and update changelog' % (__version__,)\n    )\n", "path": "scripts/hypothesistooling.py"}]}