|
4 | 4 | from getpass import getpass |
5 | 5 | import json |
6 | 6 | import requests |
| 7 | +import subprocess |
7 | 8 | import sys |
8 | 9 |
|
| 10 | +def has_uncommitted_changes(): |
| 11 | + """Check if there are uncommitted changes in a jj or git repository.""" |
| 12 | + # Try jj first — works for both native jj repos and git-backed jj repos. |
| 13 | + try: |
| 14 | + result = subprocess.run( |
| 15 | + ["jj", "diff", "--stat"], |
| 16 | + capture_output=True, |
| 17 | + text=True, |
| 18 | + ) |
| 19 | + if result.returncode == 0: |
| 20 | + # Successfully queried a jj repo; jj only tracks non-ignored files |
| 21 | + # so no extra filtering is needed. |
| 22 | + return bool(result.stdout.strip()) |
| 23 | + # jj is installed but this is not a jj repository; fall back to git. |
| 24 | + except FileNotFoundError: |
| 25 | + pass # jj is not installed |
| 26 | + |
| 27 | + # Fall back to git, ignoring untracked files. |
| 28 | + try: |
| 29 | + result = subprocess.run( |
| 30 | + ["git", "status", "--porcelain", "--untracked-files=no"], |
| 31 | + capture_output=True, |
| 32 | + text=True, |
| 33 | + ) |
| 34 | + if result.returncode == 0: |
| 35 | + return bool(result.stdout.strip()) |
| 36 | + except FileNotFoundError: |
| 37 | + pass # git is not installed |
| 38 | + |
| 39 | + return False |
| 40 | + |
| 41 | + |
9 | 42 | API_ENDPOINTS = { |
10 | 43 | "dev": "https://remote-settings-dev.allizom.org/v1/", |
11 | 44 | "stage": "https://remote-settings.allizom.org/v1/", |
|
37 | 70 | parser.print_help() |
38 | 71 | sys.exit(1) |
39 | 72 |
|
| 73 | +if has_uncommitted_changes(): |
| 74 | + if args.server == "prod": |
| 75 | + print("ERROR: There are uncommitted local changes. Please ensure all changes have been reviewed before pushing to production", file=sys.stderr) |
| 76 | + sys.exit(1) |
| 77 | + |
| 78 | + # We allow pushing with uncommitted changes on dev and stage because this |
| 79 | + # can be useful for quick tests (e.g. to help QA check something). |
| 80 | + reply = input( |
| 81 | + "\n\nWARNING: There are uncommitted local changes. Are you sure you wish to proceed? (y/N):" |
| 82 | + ).lower().strip() |
| 83 | + if not reply or reply[0] != "y": |
| 84 | + sys.exit(1) |
| 85 | + |
40 | 86 | # workspace = 'main' if args.server == 'dev' else 'main-workspace' |
41 | 87 |
|
42 | 88 | API_ENDPOINT = API_ENDPOINTS[args.server] + "buckets/%s/collections/%s/records" % ( |
|
57 | 103 |
|
58 | 104 | existingRecords = response.json() |
59 | 105 |
|
60 | | -# Handle python 2 backwards compatibility. |
61 | | -if sys.version_info[0] < 3: |
62 | | - inputFn = raw_input |
63 | | -else: |
64 | | - inputFn = input |
65 | | - |
66 | 106 |
|
67 | 107 | def getIdForRecord(record): |
68 | 108 | """ Gets or creates an id based on the given record. |
@@ -100,7 +140,7 @@ def findRecord(id, recordSet): |
100 | 140 |
|
101 | 141 | def yes_or_no(question): |
102 | 142 | while "the answer is invalid": |
103 | | - reply = str(inputFn(question + " (y/n): ")).lower().strip() |
| 143 | + reply = str(input(question + " (y/n): ")).lower().strip() |
104 | 144 | if reply[0] == "y": |
105 | 145 | return True |
106 | 146 | if reply[0] == "n": |
@@ -157,7 +197,7 @@ def strip_record(record): |
157 | 197 | recordsToRemove.append(record) |
158 | 198 |
|
159 | 199 | if len(recordsToRemove) > 0: |
160 | | - print("\Records to Remove:\n") |
| 200 | + print("\nRecords to Remove:\n") |
161 | 201 |
|
162 | 202 | for record in recordsToRemove: |
163 | 203 | print(getIdForRecord(record)) |
|
0 commit comments