ghstack 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ghstack-0.10.0 → ghstack-0.11.0}/PKG-INFO +5 -5
- {ghstack-0.10.0 → ghstack-0.11.0}/README.md +4 -4
- {ghstack-0.10.0 → ghstack-0.11.0}/pyproject.toml +1 -1
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/diff.py +2 -2
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/github_real.py +66 -29
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/land.py +18 -13
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/submit.py +4 -12
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/__init__.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/__main__.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/action.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/cache.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/checkout.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/circleci.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/circleci_real.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/cli.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/config.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/forensics.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/git.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/github.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/github_fake.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/github_schema.graphql +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/github_utils.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/gpg_sign.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/logs.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/py.typed +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/rage.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/shell.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/status.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/test_prelude.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/trailers.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/types.py +0 -0
- {ghstack-0.10.0 → ghstack-0.11.0}/src/ghstack/unlink.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ghstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Stack diff support for GitHub
|
|
5
5
|
Author: Edward Z. Yang
|
|
6
6
|
Author-email: Edward Z. Yang <ezyang@mit.edu>
|
|
@@ -69,13 +69,13 @@ resolve conflicts in *each* PR, not just for the entire stack.)
|
|
|
69
69
|
**How do I start a new feature?** Just checkout master on a new
|
|
70
70
|
branch, and start working on a fresh branch.
|
|
71
71
|
|
|
72
|
-
**
|
|
73
|
-
normal GitHub UI, as their branch bases won't be master.
|
|
74
|
-
`ghstack land $PR_URL` (or alternatively `ghstack land #PR_NUM`) to land
|
|
72
|
+
**How do I merge my changes?** Warning: You will NOT be able to merge these
|
|
73
|
+
commits using the normal GitHub UI, as their branch bases won't be master.
|
|
74
|
+
Use `ghstack land $PR_URL` (or alternatively `ghstack land #PR_NUM`) to land
|
|
75
75
|
a ghstack'ed pull request.
|
|
76
76
|
|
|
77
77
|
You can also setup a GitHub action to allow a bot land.
|
|
78
|
-
[ghstack_land_example](https://github.com/Chillee/ghstack_land_example/blob/main/.github/workflows/
|
|
78
|
+
[ghstack_land_example](https://github.com/Chillee/ghstack_land_example/blob/main/.github/workflows/ghstack_land.yml)
|
|
79
79
|
is an end-to-end example of how to do this.
|
|
80
80
|
|
|
81
81
|
## Structure of submitted pull requests
|
|
@@ -49,13 +49,13 @@ resolve conflicts in *each* PR, not just for the entire stack.)
|
|
|
49
49
|
**How do I start a new feature?** Just checkout master on a new
|
|
50
50
|
branch, and start working on a fresh branch.
|
|
51
51
|
|
|
52
|
-
**
|
|
53
|
-
normal GitHub UI, as their branch bases won't be master.
|
|
54
|
-
`ghstack land $PR_URL` (or alternatively `ghstack land #PR_NUM`) to land
|
|
52
|
+
**How do I merge my changes?** Warning: You will NOT be able to merge these
|
|
53
|
+
commits using the normal GitHub UI, as their branch bases won't be master.
|
|
54
|
+
Use `ghstack land $PR_URL` (or alternatively `ghstack land #PR_NUM`) to land
|
|
55
55
|
a ghstack'ed pull request.
|
|
56
56
|
|
|
57
57
|
You can also setup a GitHub action to allow a bot land.
|
|
58
|
-
[ghstack_land_example](https://github.com/Chillee/ghstack_land_example/blob/main/.github/workflows/
|
|
58
|
+
[ghstack_land_example](https://github.com/Chillee/ghstack_land_example/blob/main/.github/workflows/ghstack_land.yml)
|
|
59
59
|
is an end-to-end example of how to do this.
|
|
60
60
|
|
|
61
61
|
## Structure of submitted pull requests
|
|
@@ -14,7 +14,7 @@ RE_GH_METADATA = re.compile(
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
RAW_PULL_REQUEST_RESOLVED = (
|
|
17
|
-
r"(Pull Request resolved|Pull-Request-resolved): "
|
|
17
|
+
r"(Pull Request resolved|Pull-Request-resolved|Pull-Request): "
|
|
18
18
|
r"https://{github_url}/(?P<owner>[^/]+)/(?P<repo>[^/]+)/pull/(?P<number>[0-9]+)"
|
|
19
19
|
)
|
|
20
20
|
|
|
@@ -93,7 +93,7 @@ class Diff:
|
|
|
93
93
|
# be the same.
|
|
94
94
|
source_id: str
|
|
95
95
|
|
|
96
|
-
# The contents of 'Pull-Request
|
|
96
|
+
# The contents of 'Pull-Request'. This is None for
|
|
97
97
|
# diffs that haven't been submitted by ghstack. For BC reasons,
|
|
98
98
|
# this also accepts gh-metadata.
|
|
99
99
|
pull_request_resolved: Optional[PullRequestResolved]
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import re
|
|
6
|
+
import time
|
|
6
7
|
from typing import Any, Dict, Optional, Sequence, Tuple, Union
|
|
7
8
|
|
|
8
9
|
import requests
|
|
9
10
|
|
|
10
11
|
import ghstack.github
|
|
11
12
|
|
|
13
|
+
MAX_RETRIES = 5
|
|
14
|
+
INITIAL_BACKOFF_SECONDS = 60
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
class RealGitHubEndpoint(ghstack.github.GitHubEndpoint):
|
|
14
18
|
"""
|
|
@@ -155,32 +159,63 @@ class RealGitHubEndpoint(ghstack.github.GitHubEndpoint):
|
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
url = self.rest_endpoint.format(github_url=self.github_url) + "/" + path
|
|
158
|
-
logging.debug("# {} {}".format(method, url))
|
|
159
|
-
logging.debug("Request body:\n{}".format(json.dumps(kwargs, indent=1)))
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
proxies=self._proxies(),
|
|
166
|
-
verify=self.verify,
|
|
167
|
-
cert=self.cert,
|
|
168
|
-
)
|
|
163
|
+
backoff_seconds = INITIAL_BACKOFF_SECONDS
|
|
164
|
+
for attempt in range(0, MAX_RETRIES):
|
|
165
|
+
logging.debug("# {} {}".format(method, url))
|
|
166
|
+
logging.debug("Request body:\n{}".format(json.dumps(kwargs, indent=1)))
|
|
169
167
|
|
|
170
|
-
|
|
168
|
+
resp: requests.Response = getattr(requests, method)(
|
|
169
|
+
url,
|
|
170
|
+
json=kwargs,
|
|
171
|
+
headers=headers,
|
|
172
|
+
proxies=self._proxies(),
|
|
173
|
+
verify=self.verify,
|
|
174
|
+
cert=self.cert,
|
|
175
|
+
)
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
r = resp.json()
|
|
174
|
-
except ValueError:
|
|
175
|
-
logging.debug("Response body:\n{}".format(r.text))
|
|
176
|
-
raise
|
|
177
|
-
else:
|
|
178
|
-
pretty_json = json.dumps(r, indent=1)
|
|
179
|
-
logging.debug("Response JSON:\n{}".format(pretty_json))
|
|
177
|
+
logging.debug("Response status: {}".format(resp.status_code))
|
|
180
178
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
try:
|
|
180
|
+
r = resp.json()
|
|
181
|
+
except ValueError:
|
|
182
|
+
logging.debug("Response body:\n{}".format(r.text))
|
|
183
|
+
raise
|
|
184
|
+
else:
|
|
185
|
+
pretty_json = json.dumps(r, indent=1)
|
|
186
|
+
logging.debug("Response JSON:\n{}".format(pretty_json))
|
|
187
|
+
|
|
188
|
+
# Per Github rate limiting: https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#exceeding-the-rate-limit
|
|
189
|
+
if resp.status_code in (403, 429):
|
|
190
|
+
remaining_count = resp.headers.get("x-ratelimit-remaining")
|
|
191
|
+
reset_time = resp.headers.get("x-ratelimit-reset")
|
|
192
|
+
|
|
193
|
+
if remaining_count == "0" and reset_time:
|
|
194
|
+
sleep_time = int(reset_time) - int(time.time())
|
|
195
|
+
logging.warning(
|
|
196
|
+
f"Rate limit exceeded. Sleeping until reset in {sleep_time} seconds."
|
|
197
|
+
)
|
|
198
|
+
time.sleep(sleep_time)
|
|
199
|
+
continue
|
|
200
|
+
else:
|
|
201
|
+
retry_after_seconds = resp.headers.get("retry-after")
|
|
202
|
+
if retry_after_seconds:
|
|
203
|
+
sleep_time = int(retry_after_seconds)
|
|
204
|
+
logging.warning(
|
|
205
|
+
f"Secondary rate limit hit. Sleeping for {sleep_time} seconds."
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
sleep_time = backoff_seconds
|
|
209
|
+
logging.warning(
|
|
210
|
+
f"Secondary rate limit hit. Sleeping for {sleep_time} seconds (exponential backoff)."
|
|
211
|
+
)
|
|
212
|
+
backoff_seconds *= 2
|
|
213
|
+
time.sleep(sleep_time)
|
|
214
|
+
continue
|
|
215
|
+
|
|
216
|
+
if resp.status_code == 404:
|
|
217
|
+
raise ghstack.github.NotFoundError(
|
|
218
|
+
"""\
|
|
184
219
|
GitHub raised a 404 error on the request for
|
|
185
220
|
{url}.
|
|
186
221
|
Usually, this doesn't actually mean the page doesn't exist; instead, it
|
|
@@ -190,13 +225,15 @@ https://{github_url}/settings/tokens and DOUBLE CHECK that you checked
|
|
|
190
225
|
"public_repo" for permissions, and update ~/.ghstackrc with your new
|
|
191
226
|
value.
|
|
192
227
|
""".format(
|
|
193
|
-
|
|
228
|
+
url=url, github_url=self.github_url
|
|
229
|
+
)
|
|
194
230
|
)
|
|
195
|
-
)
|
|
196
231
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
232
|
+
try:
|
|
233
|
+
resp.raise_for_status()
|
|
234
|
+
except requests.HTTPError:
|
|
235
|
+
raise RuntimeError(pretty_json)
|
|
201
236
|
|
|
202
|
-
|
|
237
|
+
return r
|
|
238
|
+
|
|
239
|
+
raise RuntimeError("Exceeded maximum retries due to GitHub rate limiting")
|
|
@@ -152,11 +152,18 @@ to complain to the ghstack authors."""
|
|
|
152
152
|
sh.git("cherry-pick", "--abort")
|
|
153
153
|
raise
|
|
154
154
|
|
|
155
|
+
# All good! Push!
|
|
156
|
+
maybe_force_arg = []
|
|
157
|
+
if needs_force:
|
|
158
|
+
maybe_force_arg = ["--force-with-lease"]
|
|
159
|
+
sh.git(
|
|
160
|
+
"push", *maybe_force_arg, remote_name, f"HEAD:refs/heads/{default_branch}"
|
|
161
|
+
)
|
|
162
|
+
|
|
155
163
|
# Advance base to head to "close" the PR for all PRs.
|
|
156
|
-
# This
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
# fail
|
|
164
|
+
# This happens after the cherry-pick and push, because the cherry-picks
|
|
165
|
+
# can fail (merge conflict) and the push can also fail (race condition)
|
|
166
|
+
|
|
160
167
|
# TODO: It might be helpful to advance orig to reflect the true
|
|
161
168
|
# state of upstream at the time we are doing the land, and then
|
|
162
169
|
# directly *merge* head into base, so that the PR accurately
|
|
@@ -173,21 +180,19 @@ to complain to the ghstack authors."""
|
|
|
173
180
|
)
|
|
174
181
|
github.notify_merged(pr_resolved)
|
|
175
182
|
|
|
176
|
-
# All good! Push!
|
|
177
|
-
maybe_force_arg = []
|
|
178
|
-
if needs_force:
|
|
179
|
-
maybe_force_arg = ["--force-with-lease"]
|
|
180
|
-
sh.git(
|
|
181
|
-
"push", *maybe_force_arg, remote_name, f"HEAD:refs/heads/{default_branch}"
|
|
182
|
-
)
|
|
183
|
-
|
|
184
183
|
# Delete the branches
|
|
185
184
|
for orig_ref, _ in stack_orig_refs:
|
|
186
185
|
# TODO: regex here so janky
|
|
187
186
|
base_ref = re.sub(r"/orig$", "/base", orig_ref)
|
|
188
187
|
head_ref = re.sub(r"/orig$", "/head", orig_ref)
|
|
189
188
|
try:
|
|
190
|
-
sh.git("push", remote_name, "--delete", orig_ref, base_ref
|
|
189
|
+
sh.git("push", remote_name, "--delete", orig_ref, base_ref)
|
|
190
|
+
except RuntimeError:
|
|
191
|
+
# Whatever, keep going
|
|
192
|
+
logging.warning("Failed to delete branch, continuing", exc_info=True)
|
|
193
|
+
# Try deleting head_ref separately since often after it's merged it doesn't exist anymore
|
|
194
|
+
try:
|
|
195
|
+
sh.git("push", remote_name, "--delete", head_ref)
|
|
191
196
|
except RuntimeError:
|
|
192
197
|
# Whatever, keep going
|
|
193
198
|
logging.warning("Failed to delete branch, continuing", exc_info=True)
|
|
@@ -468,12 +468,6 @@ class Submitter:
|
|
|
468
468
|
raise RuntimeError(
|
|
469
469
|
"There appears to be no commits to process, based on the revs you passed me."
|
|
470
470
|
)
|
|
471
|
-
elif commit_count > 8 and not self.force:
|
|
472
|
-
raise RuntimeError(
|
|
473
|
-
"Cowardly refusing to handle a stack with more than eight PRs. "
|
|
474
|
-
"You are likely to get rate limited by GitHub if you try to create or "
|
|
475
|
-
"manipulate this many PRs. You can bypass this throttle using --force"
|
|
476
|
-
)
|
|
477
471
|
|
|
478
472
|
# This is not really accurate if you're doing a fancy pattern;
|
|
479
473
|
# if this is a problem file us a bug.
|
|
@@ -816,7 +810,7 @@ as it supports bidirectional syncing. However, there is no way to
|
|
|
816
810
|
convert a pre-existing PR in the old style to the new format which
|
|
817
811
|
supports bidirectional syncing. If you would like to blow away the old
|
|
818
812
|
PR and start anew, edit the Summary in the Phabricator diff to delete
|
|
819
|
-
the line 'Pull-Request
|
|
813
|
+
the line 'Pull-Request' and then run ghexport again.
|
|
820
814
|
"""
|
|
821
815
|
)
|
|
822
816
|
|
|
@@ -833,7 +827,7 @@ the Phabricator diff page? If so, please continue to use that button
|
|
|
833
827
|
to export your diff.
|
|
834
828
|
|
|
835
829
|
If you think this is in error, edit the Summary in the Phabricator diff
|
|
836
|
-
to delete the line 'Pull-Request
|
|
830
|
+
to delete the line 'Pull-Request' and then run ghexport again.
|
|
837
831
|
"""
|
|
838
832
|
)
|
|
839
833
|
else:
|
|
@@ -970,9 +964,7 @@ to disassociate the commit with the pull request, and then try again.
|
|
|
970
964
|
if self.direct:
|
|
971
965
|
trailers_to_add.append(f"ghstack-comment-id: {elab_diff.comment_id}")
|
|
972
966
|
|
|
973
|
-
trailers_to_add.append(
|
|
974
|
-
f"Pull-Request-resolved: {pull_request_resolved.url()}"
|
|
975
|
-
)
|
|
967
|
+
trailers_to_add.append(f"Pull-Request: {pull_request_resolved.url()}")
|
|
976
968
|
|
|
977
969
|
commit_msg = ghstack.trailers.interpret_trailers(
|
|
978
970
|
strip_mentions(diff.summary.rstrip()), trailers_to_add
|
|
@@ -1779,7 +1771,7 @@ is closed (likely due to being merged). Please rebase to upstream and try again
|
|
|
1779
1771
|
commit_body = RE_GHSTACK_SOURCE_ID.sub("", commit_body)
|
|
1780
1772
|
# Comment ID is not necessary; source of truth is orig commit
|
|
1781
1773
|
commit_body = RE_GHSTACK_COMMENT_ID.sub("", commit_body)
|
|
1782
|
-
# Don't store Pull request
|
|
1774
|
+
# Don't store Pull request in the PR body; it's
|
|
1783
1775
|
# unnecessary
|
|
1784
1776
|
commit_body = ghstack.diff.re_pull_request_resolved_w_sp(self.github_url).sub(
|
|
1785
1777
|
"", commit_body
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|