Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 3 | """ |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 4 | crcchecker is a tool to used to enforce that .api messages do not change. |
| 5 | API files with a semantic version < 1.0.0 are ignored. |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 6 | """ |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 7 | |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 8 | import sys |
| 9 | import os |
| 10 | import json |
| 11 | import argparse |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 12 | import re |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 13 | from subprocess import run, PIPE, check_output, CalledProcessError |
| 14 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 15 | # pylint: disable=subprocess-run-check |
| 16 | |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 17 | ROOTDIR = os.path.dirname(os.path.realpath(__file__)) + "/../.." |
| 18 | APIGENBIN = f"{ROOTDIR}/src/tools/vppapigen/vppapigen.py" |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 19 | |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 20 | |
| 21 | def crc_from_apigen(revision, filename): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 22 | """Runs vppapigen with crc plugin returning a JSON object with CRCs for |
| 23 | all APIs in filename""" |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 24 | if not revision and not os.path.isfile(filename): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 25 | print(f"skipping: {filename}", file=sys.stderr) |
Dave Barach | 592dbd0 | 2021-03-11 15:12:29 -0500 | [diff] [blame] | 26 | # Return <class 'set'> instead of <class 'dict'> |
| 27 | return {-1} |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 28 | |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 29 | if revision: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 30 | apigen = ( |
| 31 | f"{APIGENBIN} --git-revision {revision} --includedir src " |
| 32 | f"--input {filename} CRC" |
| 33 | ) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 34 | else: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 35 | apigen = f"{APIGENBIN} --includedir src --input {filename} CRC" |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 36 | returncode = run(apigen.split(), stdout=PIPE, stderr=PIPE) |
| 37 | if returncode.returncode == 2: # No such file |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 38 | print(f"skipping: {revision}:{filename} {returncode}", file=sys.stderr) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 39 | return {} |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 40 | if returncode.returncode != 0: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 41 | print( |
| 42 | f"vppapigen failed for {revision}:{filename} with " |
Ole Troan | 8987d3a | 2024-09-09 09:17:37 +0200 | [diff] [blame] | 43 | f"command:\n {apigen}\n error: {returncode.returncode}", |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 44 | file=sys.stderr, |
| 45 | ) |
Ole Troan | 8987d3a | 2024-09-09 09:17:37 +0200 | [diff] [blame] | 46 | if returncode.stderr: |
| 47 | print(f"stderr: {returncode.stderr.decode('ascii')}", file=sys.stderr) |
| 48 | if returncode.stdout: |
| 49 | print(f"stdout: {returncode.stdout.decode('ascii')}", file=sys.stderr) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 50 | sys.exit(-2) |
| 51 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 52 | return json.loads(returncode.stdout) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 53 | |
| 54 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 55 | def dict_compare(dict1, dict2): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 56 | """Compare two dictionaries returning added, removed, modified |
| 57 | and equal entries""" |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 58 | d1_keys = set(dict1.keys()) |
| 59 | d2_keys = set(dict2.keys()) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 60 | intersect_keys = d1_keys.intersection(d2_keys) |
| 61 | added = d1_keys - d2_keys |
| 62 | removed = d2_keys - d1_keys |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 63 | modified = { |
| 64 | o: (dict1[o], dict2[o]) |
| 65 | for o in intersect_keys |
| 66 | if dict1[o]["crc"] != dict2[o]["crc"] |
| 67 | } |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 68 | same = set(o for o in intersect_keys if dict1[o] == dict2[o]) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 69 | return added, removed, modified, same |
| 70 | |
| 71 | |
| 72 | def filelist_from_git_ls(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 73 | """Returns a list of all api files in the git repository""" |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 74 | filelist = [] |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 75 | git_ls = "git ls-files *.api" |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 76 | returncode = run(git_ls.split(), stdout=PIPE, stderr=PIPE) |
| 77 | if returncode.returncode != 0: |
| 78 | sys.exit(returncode.returncode) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 79 | |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 80 | for line in returncode.stdout.decode("ascii").split("\n"): |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 81 | if line: |
| 82 | filelist.append(line) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 83 | return filelist |
| 84 | |
| 85 | |
| 86 | def is_uncommitted_changes(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 87 | """Returns true if there are uncommitted changes in the repo""" |
Dave Wallace | 3a0d7d2 | 2024-03-14 21:41:00 -0400 | [diff] [blame] | 88 | # Don't run this check in the Jenkins CI |
| 89 | if os.getenv("FDIOTOOLS_IMAGE") is None: |
| 90 | git_status = "git status --porcelain -uno" |
| 91 | returncode = run(git_status.split(), stdout=PIPE, stderr=PIPE) |
| 92 | if returncode.returncode != 0: |
| 93 | sys.exit(returncode.returncode) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 94 | |
Dave Wallace | 3a0d7d2 | 2024-03-14 21:41:00 -0400 | [diff] [blame] | 95 | if returncode.stdout: |
| 96 | return True |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 97 | return False |
| 98 | |
| 99 | |
| 100 | def filelist_from_git_grep(filename): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 101 | """Returns a list of api files that this <filename> api files imports.""" |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 102 | filelist = [] |
| 103 | try: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 104 | returncode = check_output( |
| 105 | f'git grep -e "import .*{filename}"' " -- *.api", shell=True |
| 106 | ) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 107 | except CalledProcessError: |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 108 | return [] |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 109 | for line in returncode.decode("ascii").split("\n"): |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 110 | if line: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 111 | filename, _ = line.split(":") |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 112 | filelist.append(filename) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 113 | return filelist |
| 114 | |
| 115 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 116 | def filelist_from_patchset(pattern): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 117 | """Returns list of api files in changeset and the list of api |
| 118 | files they import.""" |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 119 | filelist = [] |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 120 | git_cmd = ( |
| 121 | "((git diff HEAD~1.. --name-only;git ls-files -m) | " |
| 122 | 'sort -u | grep "\\.api$")' |
| 123 | ) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 124 | try: |
| 125 | res = check_output(git_cmd, shell=True) |
| 126 | except CalledProcessError: |
| 127 | return [] |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 128 | |
| 129 | # Check for dependencies (imports) |
| 130 | imported_files = [] |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 131 | for line in res.decode("ascii").split("\n"): |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 132 | if not line: |
| 133 | continue |
| 134 | if not re.search(pattern, line): |
| 135 | continue |
| 136 | filelist.append(line) |
| 137 | imported_files.extend(filelist_from_git_grep(os.path.basename(line))) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 138 | |
| 139 | filelist.extend(imported_files) |
| 140 | return set(filelist) |
| 141 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 142 | |
| 143 | def is_deprecated(message): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 144 | """Given a message, return True if message is deprecated""" |
| 145 | if "options" in message: |
| 146 | if "deprecated" in message["options"]: |
Andrew Yourtchenko | 6a3d4cc | 2020-09-22 15:11:51 +0000 | [diff] [blame] | 147 | return True |
| 148 | # recognize the deprecated format |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 149 | if ( |
| 150 | "status" in message["options"] |
| 151 | and message["options"]["status"] == "deprecated" |
| 152 | ): |
Andrew Yourtchenko | 6a3d4cc | 2020-09-22 15:11:51 +0000 | [diff] [blame] | 153 | print("WARNING: please use 'option deprecated;'") |
| 154 | return True |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 155 | return False |
| 156 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 157 | |
| 158 | def is_in_progress(message): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 159 | """Given a message, return True if message is marked as in_progress""" |
| 160 | if "options" in message: |
| 161 | if "in_progress" in message["options"]: |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 162 | return True |
Andrew Yourtchenko | 6a3d4cc | 2020-09-22 15:11:51 +0000 | [diff] [blame] | 163 | # recognize the deprecated format |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 164 | if ( |
| 165 | "status" in message["options"] |
| 166 | and message["options"]["status"] == "in_progress" |
| 167 | ): |
Andrew Yourtchenko | 6a3d4cc | 2020-09-22 15:11:51 +0000 | [diff] [blame] | 168 | print("WARNING: please use 'option in_progress;'") |
| 169 | return True |
| 170 | return False |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 171 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 172 | |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 173 | def report(new, old): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 174 | """Given a dictionary of new crcs and old crcs, print all the |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 175 | added, removed, modified, in-progress, deprecated messages. |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 176 | Return the number of backwards incompatible changes made.""" |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 177 | |
| 178 | # pylint: disable=too-many-branches |
| 179 | |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 180 | new.pop("_version", None) |
| 181 | old.pop("_version", None) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 182 | added, removed, modified, _ = dict_compare(new, old) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 183 | backwards_incompatible = 0 |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 184 | |
Andrew Yourtchenko | 8b0cd69 | 2020-09-16 09:48:59 +0000 | [diff] [blame] | 185 | # print the full list of in-progress messages |
| 186 | # they should eventually either disappear of become supported |
| 187 | for k in new.keys(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 188 | newversion = int(new[k]["version"]) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 189 | if newversion == 0 or is_in_progress(new[k]): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 190 | print(f"in-progress: {k}") |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 191 | for k in added: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 192 | print(f"added: {k}") |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 193 | for k in removed: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 194 | oldversion = int(old[k]["version"]) |
| 195 | if oldversion > 0 and not is_deprecated(old[k]) and not is_in_progress(old[k]): |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 196 | backwards_incompatible += 1 |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 197 | print(f"removed: ** {k}") |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 198 | else: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 199 | print(f"removed: {k}") |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 200 | for k in modified.keys(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 201 | oldversion = int(old[k]["version"]) |
| 202 | newversion = int(new[k]["version"]) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 203 | if oldversion > 0 and not is_in_progress(old[k]): |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 204 | backwards_incompatible += 1 |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 205 | print(f"modified: ** {k}") |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 206 | else: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 207 | print(f"modified: {k}") |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 208 | |
| 209 | # check which messages are still there but were marked for deprecation |
| 210 | for k in new.keys(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 211 | newversion = int(new[k]["version"]) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 212 | if newversion > 0 and is_deprecated(new[k]): |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 213 | if k in old: |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 214 | if not is_deprecated(old[k]): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 215 | print(f"deprecated: {k}") |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 216 | else: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 217 | print(f"added+deprecated: {k}") |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 218 | |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 219 | return backwards_incompatible |
| 220 | |
| 221 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 222 | def check_patchset(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 223 | """Compare the changes to API messages in this changeset. |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 224 | Ignores API files with version < 1.0.0. |
| 225 | Only considers API files located under the src directory in the repo. |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 226 | """ |
| 227 | files = filelist_from_patchset("^src/") |
| 228 | revision = "HEAD~1" |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 229 | |
| 230 | oldcrcs = {} |
| 231 | newcrcs = {} |
| 232 | for filename in files: |
| 233 | # Ignore files that have version < 1.0.0 |
| 234 | _ = crc_from_apigen(None, filename) |
Dave Barach | 592dbd0 | 2021-03-11 15:12:29 -0500 | [diff] [blame] | 235 | # Ignore removed files |
| 236 | if isinstance(_, set) == 0: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 237 | if isinstance(_, set) == 0 and _["_version"]["major"] == "0": |
Dave Barach | 592dbd0 | 2021-03-11 15:12:29 -0500 | [diff] [blame] | 238 | continue |
| 239 | newcrcs.update(_) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 240 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 241 | oldcrcs.update(crc_from_apigen(revision, filename)) |
| 242 | |
| 243 | backwards_incompatible = report(newcrcs, oldcrcs) |
| 244 | if backwards_incompatible: |
| 245 | # alert on changing production API |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 246 | print( |
| 247 | "crcchecker: Changing production APIs in an incompatible way", |
| 248 | file=sys.stderr, |
| 249 | ) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 250 | sys.exit(-1) |
| 251 | else: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 252 | print("*" * 67) |
| 253 | print("* VPP CHECKAPI SUCCESSFULLY COMPLETED") |
| 254 | print("*" * 67) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 255 | |
| 256 | |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 257 | def main(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 258 | """Main entry point.""" |
| 259 | parser = argparse.ArgumentParser(description="VPP CRC checker.") |
| 260 | parser.add_argument("--git-revision", help="Git revision to compare against") |
| 261 | parser.add_argument( |
| 262 | "--dump-manifest", action="store_true", help="Dump CRC for all messages" |
| 263 | ) |
| 264 | parser.add_argument( |
| 265 | "--check-patchset", |
| 266 | action="store_true", |
| 267 | help="Check patchset for backwards incompatbile changes", |
| 268 | ) |
| 269 | parser.add_argument("files", nargs="*") |
| 270 | parser.add_argument("--diff", help="Files to compare (on filesystem)", nargs=2) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 271 | |
| 272 | args = parser.parse_args() |
| 273 | |
| 274 | if args.diff and args.files: |
| 275 | parser.print_help() |
| 276 | sys.exit(-1) |
| 277 | |
| 278 | # Diff two files |
| 279 | if args.diff: |
| 280 | oldcrcs = crc_from_apigen(None, args.diff[0]) |
| 281 | newcrcs = crc_from_apigen(None, args.diff[1]) |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 282 | backwards_incompatible = report(newcrcs, oldcrcs) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 283 | sys.exit(0) |
| 284 | |
| 285 | # Dump CRC for messages in given files / revision |
| 286 | if args.dump_manifest: |
| 287 | files = args.files if args.files else filelist_from_git_ls() |
| 288 | crcs = {} |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 289 | for filename in files: |
| 290 | crcs.update(crc_from_apigen(args.git_revision, filename)) |
| 291 | for k, value in crcs.items(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 292 | print(f"{k}: {value}") |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 293 | sys.exit(0) |
| 294 | |
| 295 | # Find changes between current patchset and given revision (previous) |
| 296 | if args.check_patchset: |
| 297 | if args.git_revision: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 298 | print("Argument git-revision ignored", file=sys.stderr) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 299 | # Check there are no uncomitted changes |
| 300 | if is_uncommitted_changes(): |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 301 | print("Please stash or commit changes in workspace", file=sys.stderr) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 302 | sys.exit(-1) |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 303 | check_patchset() |
| 304 | sys.exit(0) |
| 305 | |
| 306 | # Find changes between current workspace and revision |
| 307 | # Find changes between a given file and a revision |
| 308 | files = args.files if args.files else filelist_from_git_ls() |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 309 | |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 310 | revision = args.git_revision if args.git_revision else "HEAD~1" |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 311 | |
| 312 | oldcrcs = {} |
| 313 | newcrcs = {} |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 314 | for file in files: |
| 315 | newcrcs.update(crc_from_apigen(None, file)) |
| 316 | oldcrcs.update(crc_from_apigen(revision, file)) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 317 | |
Andrew Yourtchenko | 62bd50d | 2020-09-11 17:40:52 +0000 | [diff] [blame] | 318 | backwards_incompatible = report(newcrcs, oldcrcs) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 319 | |
| 320 | if args.check_patchset: |
| 321 | if backwards_incompatible: |
| 322 | # alert on changing production API |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 323 | print( |
| 324 | "crcchecker: Changing production APIs in an incompatible way", |
| 325 | file=sys.stderr, |
| 326 | ) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 327 | sys.exit(-1) |
| 328 | else: |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 329 | print("*" * 67) |
| 330 | print("* VPP CHECKAPI SUCCESSFULLY COMPLETED") |
| 331 | print("*" * 67) |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 332 | |
Ole Troan | ab9f573 | 2020-12-15 10:19:25 +0100 | [diff] [blame] | 333 | |
Klement Sekera | d9b0c6f | 2022-04-26 19:02:15 +0200 | [diff] [blame] | 334 | if __name__ == "__main__": |
Ole Troan | 5c318c7 | 2020-05-05 12:23:47 +0200 | [diff] [blame] | 335 | main() |