Skip to content

Commit e1eff1f

Browse files
committed
fix(vcs): annotate remote operation for validation
This makes the validation explcitit and not opt-in.
1 parent 34190da commit e1eff1f

13 files changed

Lines changed: 426 additions & 140 deletions

File tree

weblate/addons/git.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def squash_repo(
4444
author: str | None = None,
4545
) -> None:
4646
message = self.get_squash_commit_message(repository, "%B", remote)
47-
repository.execute(["reset", "--mixed", remote])
47+
repository.execute(["reset", "--mixed", remote], remote_op="none")
4848
# Can happen for added and removed translation
4949
component.commit_files(
5050
author=author, message=message, signals=False, skip_push=True
@@ -78,7 +78,7 @@ def get_git_commit_messages(
7878
if filenames:
7979
command += ["--", *filenames]
8080

81-
return repository.execute(command)
81+
return repository.execute(command, remote_op="none")
8282

8383
def get_squash_commit_message(
8484
self,
@@ -100,7 +100,7 @@ def get_squash_commit_message(
100100

101101
trailer_lines = set()
102102
change_id_line = None
103-
for trailer in repository.execute(command).split("\n"):
103+
for trailer in repository.execute(command, remote_op="none").split("\n"):
104104
# Skip blank lines
105105
if not trailer.strip():
106106
continue
@@ -158,7 +158,7 @@ def squash_language(self, component: Component, repository: GitRepository) -> No
158158
repository, "%B", remote, filenames
159159
)
160160

161-
repository.execute(["reset", "--mixed", remote])
161+
repository.execute(["reset", "--mixed", remote], remote_op="none")
162162

163163
for code, message in messages.items():
164164
if not message:
@@ -178,7 +178,7 @@ def squash_file(self, component: Component, repository: GitRepository) -> None:
178178
repository, "%B", remote, [filename]
179179
)
180180

181-
repository.execute(["reset", "--mixed", remote])
181+
repository.execute(["reset", "--mixed", remote], remote_op="none")
182182

183183
for filename, message in messages.items():
184184
if not message:
@@ -194,7 +194,8 @@ def squash_author(self, component: Component, repository: GitRepository) -> None
194194
x.split(None, 1)
195195
for x in reversed(
196196
repository.execute(
197-
["log", "--no-merges", "--format=%H %aE", f"{remote}..HEAD"]
197+
["log", "--no-merges", "--format=%H %aE", f"{remote}..HEAD"],
198+
remote_op="none",
198199
).splitlines()
199200
)
200201
]
@@ -204,9 +205,9 @@ def squash_author(self, component: Component, repository: GitRepository) -> None
204205
repository.delete_branch(tmp)
205206
try:
206207
# Create local branch for upstream
207-
repository.execute(["branch", tmp, remote])
208+
repository.execute(["branch", tmp, remote], remote_op="none")
208209
# Checkout upstream branch
209-
repository.execute(["checkout", tmp])
210+
repository.execute(["checkout", tmp], remote_op="none")
210211
while commits:
211212
commit, author = commits.pop(0)
212213
# Remember current revision for final squash
@@ -216,11 +217,12 @@ def squash_author(self, component: Component, repository: GitRepository) -> None
216217
try:
217218
repository.execute(
218219
["cherry-pick", "--empty=drop", commit, *gpg_sign],
220+
remote_op="none",
219221
environment={"WEBLATE_MERGE_SKIP": "1"},
220222
)
221223
except RepositoryError:
222224
if repository.has_git_file("CHERRY_PICK_HEAD"):
223-
repository.execute(["cherry-pick", "--abort"])
225+
repository.execute(["cherry-pick", "--abort"], remote_op="none")
224226
raise
225227
handled = []
226228
# Pick other commits by same author
@@ -230,14 +232,17 @@ def squash_author(self, component: Component, repository: GitRepository) -> None
230232
try:
231233
repository.execute(
232234
["cherry-pick", "--empty=drop", other[0], *gpg_sign],
235+
remote_op="none",
233236
environment={"WEBLATE_MERGE_SKIP": "1"},
234237
)
235238
handled.append(i)
236239
except RepositoryError:
237240
# If fails, continue to another author, we will
238241
# pick this commit later (it depends on some other)
239242
if repository.has_git_file("CHERRY_PICK_HEAD"):
240-
repository.execute(["cherry-pick", "--abort"])
243+
repository.execute(
244+
["cherry-pick", "--abort"], remote_op="none"
245+
)
241246
break
242247
# Remove processed commits from list
243248
for i in reversed(handled):
@@ -246,15 +251,15 @@ def squash_author(self, component: Component, repository: GitRepository) -> None
246251
self.squash_repo(component, repository, base, author)
247252

248253
# Update working copy with squashed commits
249-
repository.execute(["checkout", repository.branch])
250-
repository.execute(["reset", "--hard", tmp])
254+
repository.execute(["checkout", repository.branch], remote_op="none")
255+
repository.execute(["reset", "--hard", tmp], remote_op="none")
251256
repository.delete_branch(tmp)
252257

253258
except Exception:
254259
report_error("Failed squash", project=component.project)
255260
# Revert to original branch without any changes
256-
repository.execute(["reset", "--hard"])
257-
repository.execute(["checkout", repository.branch])
261+
repository.execute(["reset", "--hard"], remote_op="none")
262+
repository.execute(["checkout", repository.branch], remote_op="none")
258263
repository.delete_branch(tmp)
259264

260265
def post_commit(

weblate/addons/tests.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3373,7 +3373,10 @@ def test_cleanup(self) -> None:
33733373
addon = CleanupAddon.create(component=self.component)
33743374
# Unshallow the local repo
33753375
with self.component.repository.lock:
3376-
self.component.repository.execute(["fetch", "--unshallow", "origin"])
3376+
self.component.repository.execute(
3377+
["fetch", "--unshallow", "origin"],
3378+
remote_op="pull",
3379+
)
33773380
addon.post_update(
33783381
self.component, "da07dc0dc7052dc44eadfa8f3a2f2609ec634303", False
33793382
)
@@ -3387,7 +3390,10 @@ def test_update(self) -> None:
33873390
rev = self.component.repository.last_revision
33883391
# Unshallow the local repo
33893392
with self.component.repository.lock:
3390-
self.component.repository.execute(["fetch", "--unshallow", "origin"])
3393+
self.component.repository.execute(
3394+
["fetch", "--unshallow", "origin"],
3395+
remote_op="pull",
3396+
)
33913397
addon.post_update(
33923398
self.component, "da07dc0dc7052dc44eadfa8f3a2f2609ec634303", False
33933399
)

weblate/api/tests.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1953,7 +1953,9 @@ def __init__(self) -> None:
19531953
def has_branch(self, _branch: str) -> bool:
19541954
return False
19551955

1956-
def execute(self, args: list[str]) -> None:
1956+
def execute(self, args: list[str], *, remote_op: str) -> None:
1957+
if remote_op != "none":
1958+
raise AssertionError(remote_op)
19571959
calls.append(args)
19581960

19591961
def clean_revision_cache(self) -> None:

weblate/gitexport/tests.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ def test_ignore_missing_requested_have(self) -> None:
306306
self.mark_component_shallow()
307307
present_revision = self.component.repository.execute(
308308
["rev-parse", "HEAD"],
309+
remote_op="none",
309310
needs_lock=False,
310311
).strip()
311312
body = (
@@ -320,6 +321,7 @@ def test_ignore_missing_requested_have_with_present_want(self) -> None:
320321
self.mark_component_shallow()
321322
present_revision = self.component.repository.execute(
322323
["rev-parse", "HEAD"],
324+
remote_op="none",
323325
needs_lock=False,
324326
).strip()
325327
body = (
@@ -333,6 +335,7 @@ def test_ignore_missing_requested_have_with_present_want(self) -> None:
333335
def test_stop_parsing_after_initial_want_block(self) -> None:
334336
present_revision = self.component.repository.execute(
335337
["rev-parse", "HEAD"],
338+
remote_op="none",
336339
needs_lock=False,
337340
).strip()
338341
body = (
@@ -410,6 +413,7 @@ def test_missing_have_does_not_short_circuit_upload_pack(self) -> None:
410413
self.mark_component_shallow()
411414
present_revision = self.component.repository.execute(
412415
["rev-parse", "HEAD"],
416+
remote_op="none",
413417
needs_lock=False,
414418
).strip()
415419
body = (
@@ -533,6 +537,7 @@ def create_export_commit(self) -> str:
533537
self.component.repository.commit("Test export change", files=[filename])
534538
return self.component.repository.execute(
535539
["rev-parse", "HEAD"],
540+
remote_op="none",
536541
needs_lock=False,
537542
).strip()
538543

weblate/gitexport/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ def get_precheck_failure_reason(component: Component, body: bytes) -> str | None
229229
try:
230230
output = execute(
231231
["cat-file", "--batch-check"],
232+
remote_op="none",
232233
needs_lock=False,
233234
stdin="".join(f"{revision}\n" for revision in wanted_revisions),
234235
)

weblate/trans/component_copy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ def normalize_local_copy_branch(component: Component) -> None:
122122

123123
with repository.lock:
124124
if repository.has_branch(component.branch):
125-
repository.execute(["checkout", component.branch])
125+
repository.execute(["checkout", component.branch], remote_op="none")
126126
else:
127-
repository.execute(["checkout", "-B", component.branch])
127+
repository.execute(["checkout", "-B", component.branch], remote_op="none")
128128
repository.branch = component.branch
129129
repository.clean_revision_cache()
130130

weblate/trans/tests/test_component.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,8 @@ def restore_local_missing_translation_files(
12321232
self.local_missing_translation_contents[translation.pk]
12331233
)
12341234
translation.component.repository.execute(
1235-
["add", "--force", "--", translation.filename]
1235+
["add", "--force", "--", translation.filename],
1236+
remote_op="none",
12361237
)
12371238

12381239
def test_reset_keep_recreates_missing_translation_file(self) -> None:

weblate/trans/tests/test_git_views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ def setUp(self) -> None:
175175
super().setUp()
176176
repo = self.component.repository
177177
with repo.lock:
178-
repo.execute(["branch", "--delete", "--remotes", "origin/main"])
178+
repo.execute(
179+
["branch", "--delete", "--remotes", "origin/main"],
180+
remote_op="none",
181+
)
179182

180183

181184
class GitBrokenComponentTest(GitBrokenProjectTest):

weblate/trans/tests/test_remote.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,10 @@ def test_pending_changes_preserved_on_non_translation_update(self) -> None:
595595
"# Test Project\n\nThis is a test README.\n", encoding="utf-8"
596596
)
597597
with self.component.repository.lock:
598-
self.component.repository.execute(["add", "README.md"])
598+
self.component.repository.execute(["add", "README.md"], remote_op="none")
599599
self.component.repository.execute(
600-
["commit", "-m", "Add README.md (non-translation file)"]
600+
["commit", "-m", "Add README.md (non-translation file)"],
601+
remote_op="none",
601602
)
602603
self.component.repository.push(self.component.push_branch)
603604

0 commit comments

Comments
 (0)