|
3 | 3 | # Utility for viewing and managing versions of cargo workspaces and crates.
|
4 | 4 | # For workspaces, it assumes that all crate members use a single shared version.
|
5 | 5 | #
|
6 |
| -# usage: |
7 |
| -# cargo_version.py [-h] [-p PROJECT] [-r] [-n {major,minor,patch}] |
8 |
| -# [-s SET] [-o] [-m PRERELEASE] |
| 6 | +# usage: cargo-version.py [-h] [-p PROJECT] [-r] [-n {major,minor,patch}] [-s SET] [-o] [-m PRERELEASE] |
9 | 7 | #
|
10 | 8 | # Change versions of cargo projects.
|
11 | 9 | #
|
|
21 | 19 | # -m PRERELEASE, --prerelease PRERELEASE
|
22 | 20 | # Set pre-prelease string.
|
23 | 21 | #
|
24 |
| - |
25 |
| -import argparse |
26 |
| -import semver |
27 | 22 | import toml
|
| 23 | +import semver |
| 24 | +import argparse |
28 | 25 |
|
29 | 26 |
|
30 | 27 | class Crate:
|
31 |
| - def __init__(self, path, name, version, dependencies): |
32 |
| - self.path = path |
33 |
| - self.name = name |
34 |
| - self.version = version |
35 |
| - self.dependencies = dependencies |
36 |
| - |
37 |
| - def with_dependencies(self, names): |
38 |
| - deps = {k: v for k, v in self.dependencies.items() if k in names} |
39 |
| - return Crate(self.path, self.name, self.version, deps) |
40 |
| - |
41 |
| - @classmethod |
42 |
| - def finalize(cls, version): |
43 |
| - return str(semver.VersionInfo.parse(version).finalize_version()) |
44 |
| - |
45 |
| - @classmethod |
46 |
| - def bump_level(cls, version, level): |
47 |
| - parsed_version = semver.VersionInfo.parse(version) |
48 |
| - if level == 'major': |
49 |
| - return str(parsed_version.bump_major()) |
50 |
| - if level == 'minor': |
51 |
| - return str(parsed_version.bump_minor()) |
52 |
| - if level == 'patch': |
53 |
| - return str(parsed_version.bump_patch()) |
54 |
| - |
55 |
| - ### remove the .1 suffix that semver always adds to the prererelease before returning |
56 |
| - return str(parsed_version.bump_prerelease('nightly'))[:-2] |
57 |
| - |
58 |
| - @classmethod |
59 |
| - def prerelease(cls, version, prerelease): |
60 |
| - parsed_version = semver.VersionInfo.parse(version) |
61 |
| - return str(semver.VersionInfo(parsed_version.major, parsed_version.minor, |
62 |
| - parsed_version.patch, prerelease)) |
63 |
| - |
64 |
| - def finalize_version(self): |
65 |
| - return Crate(self.path, self.name, Crate.finalize(self.version), |
66 |
| - self.dependencies.copy()) |
67 |
| - |
68 |
| - def bump_version(self, level): |
69 |
| - return Crate(self.path, self.name, Crate.bump_level(self.version, level), |
70 |
| - self.dependencies.copy()) |
71 |
| - |
72 |
| - def set_version(self, version): |
73 |
| - return Crate(self.path, self.name, version, self.dependencies.copy()) |
74 |
| - |
75 |
| - def set_prerelease(self, prerelease): |
76 |
| - return Crate(self.path, self.name, Crate.prerelease(self.version, prerelease), |
77 |
| - self.dependencies.copy()) |
78 |
| - |
79 |
| - def next_version(self): |
80 |
| - return Crate(self.path, self.name, |
81 |
| - str(semver.VersionInfo.parse(self.version).next_version('patch')), |
82 |
| - self.dependencies.copy()) |
83 |
| - |
84 |
| - def show_version(self): |
85 |
| - return self.version |
86 |
| - |
87 |
| - def save(self, previous): |
88 |
| - contents = [] |
89 |
| - cargo_file = f"{self.path}/Cargo.toml" |
90 |
| - with open(cargo_file, mode='r', encoding='utf-8') as cargo_file_read: |
91 |
| - for line in cargo_file_read.readlines(): |
92 |
| - if line.startswith("version"): |
93 |
| - line = line.replace(previous.version, self.version) |
| 28 | + def __init__(self, path, name, version, dependencies): |
| 29 | + self.path = path |
| 30 | + self.name = name |
| 31 | + self.version = version |
| 32 | + self.dependencies = dependencies |
| 33 | + |
| 34 | + def with_dependencies(self, names): |
| 35 | + deps = {k: v for k, v in self.dependencies.items() if k in names} |
| 36 | + return Crate(self.path, self.name, self.version, deps) |
| 37 | + |
| 38 | + @classmethod |
| 39 | + def finalize(cls, version): |
| 40 | + return str(semver.VersionInfo.parse(version).finalize_version()) |
| 41 | + |
| 42 | + @classmethod |
| 43 | + def bump_level(cls, version, level): |
| 44 | + v = semver.VersionInfo.parse(version) |
| 45 | + if level == "major": |
| 46 | + return str(v.bump_major()) |
| 47 | + elif level == "minor": |
| 48 | + return str(v.bump_minor()) |
| 49 | + elif level == "patch": |
| 50 | + return str(v.bump_patch()) |
94 | 51 | else:
|
95 |
| - for dname, dversion in self.dependencies.items(): |
96 |
| - if line.startswith(dname): |
97 |
| - line = line.replace(previous.dependencies[dname], dversion) |
98 |
| - contents.append(line) |
99 |
| - |
100 |
| - with open(cargo_file, mode='w', encoding='utf-8') as cargo_file_write: |
101 |
| - cargo_file_write.write(''.join(contents)) |
102 |
| - |
103 |
| - def __str__(self): |
104 |
| - return f'Crate({self.path}, {self.name}, {self.version}, {self.dependencies})' |
| 52 | + return str(v.bump_prerelease("nightly"))[:-2] # remove the .1 suffix that semver always adds to the prererelease. |
| 53 | + |
| 54 | + @classmethod |
| 55 | + def prerelease(cls, version, prerelease): |
| 56 | + v = semver.VersionInfo.parse(version) |
| 57 | + return str(semver.VersionInfo(v.major, v.minor, v.patch, prerelease)) |
| 58 | + |
| 59 | + def finalize_version(self): |
| 60 | + return Crate( |
| 61 | + self.path, self.name, Crate.finalize(self.version), self.dependencies.copy() |
| 62 | + ) |
| 63 | + |
| 64 | + def bump_version(self, level): |
| 65 | + return Crate( |
| 66 | + self.path, |
| 67 | + self.name, |
| 68 | + Crate.bump_level(self.version, level), |
| 69 | + self.dependencies.copy(), |
| 70 | + ) |
| 71 | + |
| 72 | + def set_version(self, version): |
| 73 | + return Crate(self.path, self.name, version, self.dependencies.copy()) |
| 74 | + |
| 75 | + def set_prerelease(self, prerelease): |
| 76 | + return Crate( |
| 77 | + self.path, |
| 78 | + self.name, |
| 79 | + Crate.prerelease(self.version, prerelease), |
| 80 | + self.dependencies.copy(), |
| 81 | + ) |
| 82 | + |
| 83 | + def next_version(self): |
| 84 | + return Crate( |
| 85 | + self.path, |
| 86 | + self.name, |
| 87 | + str(semver.VersionInfo.parse(self.version).next_version("patch")), |
| 88 | + self.dependencies.copy(), |
| 89 | + ) |
| 90 | + |
| 91 | + def show_version(self): |
| 92 | + return self.version |
| 93 | + |
| 94 | + def save(self, previous): |
| 95 | + contents = [] |
| 96 | + cargo_file = f"{self.path}/Cargo.toml" |
| 97 | + with open(cargo_file, "r") as r: |
| 98 | + for line in r.readlines(): |
| 99 | + if line.startswith("version"): |
| 100 | + line = line.replace(previous.version, self.version) |
| 101 | + else: |
| 102 | + for dname, dversion in self.dependencies.items(): |
| 103 | + if line.startswith(dname): |
| 104 | + line = line.replace(previous.dependencies[dname], dversion) |
| 105 | + contents.append(line) |
| 106 | + with open(cargo_file, "w") as w: |
| 107 | + w.write("".join(contents)) |
| 108 | + |
| 109 | + def __str__(self): |
| 110 | + return f"Crate({self.path}, {self.name}, {self.version}, {self.dependencies})" |
105 | 111 |
|
106 | 112 |
|
107 | 113 | class Workspace:
|
108 |
| - def __init__(self, crates): |
109 |
| - names = {c.name for c in crates} |
110 |
| - self.crates = {c.name: c.with_dependencies(names) for c in crates} |
| 114 | + def __init__(self, crates): |
| 115 | + names = set([c.name for c in crates]) |
| 116 | + self.crates = {c.name: c.with_dependencies(names) for c in crates} |
111 | 117 |
|
112 |
| - def finalize_version(self): |
113 |
| - crates = {c.name: c.finalize_version() for c in self.crates.values()} |
114 |
| - return Workspace(Workspace.update_dependencies(crates).values()) |
| 118 | + def finalize_version(self): |
| 119 | + crates = {c.name: c.finalize_version() for c in self.crates.values()} |
| 120 | + return Workspace(Workspace.update_dependencies(crates).values()) |
115 | 121 |
|
116 |
| - def bump_version(self, level): |
117 |
| - crates = {c.name: c.bump_version(level) for c in self.crates.values()} |
118 |
| - return Workspace(Workspace.update_dependencies(crates).values()) |
| 122 | + def bump_version(self, level): |
| 123 | + crates = {c.name: c.bump_version(level) for c in self.crates.values()} |
| 124 | + return Workspace(Workspace.update_dependencies(crates).values()) |
119 | 125 |
|
120 |
| - def set_version(self, version): |
121 |
| - crates = {c.name: c.set_version(version) for c in self.crates.values()} |
122 |
| - return Workspace(Workspace.update_dependencies(crates).values()) |
| 126 | + def set_version(self, version): |
| 127 | + crates = {c.name: c.set_version(version) for c in self.crates.values()} |
| 128 | + return Workspace(Workspace.update_dependencies(crates).values()) |
123 | 129 |
|
124 |
| - def set_prerelease(self, prerelease): |
125 |
| - crates = {c.name: c.set_prerelease(prerelease) for c in self.crates.values()} |
126 |
| - return Workspace(Workspace.update_dependencies(crates).values()) |
| 130 | + def set_prerelease(self, prerelease): |
| 131 | + crates = {c.name: c.set_prerelease(prerelease) for c in self.crates.values()} |
| 132 | + return Workspace(Workspace.update_dependencies(crates).values()) |
127 | 133 |
|
128 |
| - def next_version(self): |
129 |
| - crates = {c.name: c.next_version() for c in self.crates.values()} |
130 |
| - return Workspace(Workspace.update_dependencies(crates).values()) |
| 134 | + def next_version(self): |
| 135 | + crates = {c.name: c.next_version() for c in self.crates.values()} |
| 136 | + return Workspace(Workspace.update_dependencies(crates).values()) |
131 | 137 |
|
132 |
| - def show_version(self): |
133 |
| - for crate in self.crates.values(): |
134 |
| - return crate.show_version() |
135 |
| - return "0.0.0" |
| 138 | + def show_version(self): |
| 139 | + for c in self.crates.values(): |
| 140 | + return c.show_version() |
| 141 | + return "0.0.0" |
136 | 142 |
|
137 |
| - @classmethod |
138 |
| - def update_dependencies(cls, crate_dict): |
139 |
| - for crate in crate_dict.values(): |
140 |
| - for dep in crate.dependencies.keys(): |
141 |
| - crate.dependencies[dep] = crate_dict[dep].version |
142 |
| - return crate_dict |
| 143 | + @classmethod |
| 144 | + def update_dependencies(cls, crate_dict): |
| 145 | + for crate in crate_dict.values(): |
| 146 | + for dep in crate.dependencies.keys(): |
| 147 | + crate.dependencies[dep] = crate_dict[dep].version |
| 148 | + return crate_dict |
143 | 149 |
|
144 |
| - def __str__(self): |
145 |
| - return f'Workspace({[str(c) for c in self.crates.values()]})' |
| 150 | + def __str__(self): |
| 151 | + return f"Workspace({[str(c) for c in self.crates.values()]})" |
146 | 152 |
|
147 |
| - def save(self, previous): |
148 |
| - for crate_key in self.crates.keys(): |
149 |
| - self.crates[crate_key].save(previous.crates[crate_key]) |
| 153 | + def save(self, previous): |
| 154 | + for cn in self.crates.keys(): |
| 155 | + self.crates[cn].save(previous.crates[cn]) |
150 | 156 |
|
151 | 157 |
|
152 | 158 | def load(root):
|
153 |
| - root_cargo_file = toml.load(f"{root}/Cargo.toml") |
154 |
| - if "workspace" in root_cargo_file: |
155 |
| - return Workspace([load(f"{root}/{path}") |
156 |
| - for path in root_cargo_file["workspace"]["members"]]) |
157 |
| - |
158 |
| - return Crate(path=root, name=root_cargo_file["package"]["name"], |
159 |
| - version=root_cargo_file["package"]["version"], |
160 |
| - dependencies={dn: root_cargo_file["dependencies"][dn]["version"] |
161 |
| - for dn in root_cargo_file["dependencies"] |
162 |
| - if "version" in root_cargo_file["dependencies"][dn]}) |
| 159 | + r = toml.load(f"{root}/Cargo.toml") |
| 160 | + if "workspace" in r: |
| 161 | + return Workspace([load(f"{root}/{path}") for path in r["workspace"]["members"]]) |
| 162 | + else: |
| 163 | + return Crate( |
| 164 | + path=root, |
| 165 | + name=r["package"]["name"], |
| 166 | + version=r["package"]["version"], |
| 167 | + dependencies={ |
| 168 | + dn: r["dependencies"][dn]["version"] |
| 169 | + for dn in r["dependencies"] |
| 170 | + if "version" in r["dependencies"][dn] |
| 171 | + }, |
| 172 | + ) |
163 | 173 |
|
164 | 174 |
|
165 | 175 | def parse_args():
|
166 |
| - parser = argparse.ArgumentParser(description="Change versions of cargo projects.") |
167 |
| - parser.add_argument("-p", "--project", help="Project folder", default=".") |
168 |
| - parser.add_argument("-r", "--release", help="Version", action="store_true") |
169 |
| - parser.add_argument("-n", "--next", help="Version", choices=['major', 'minor', 'patch']) |
170 |
| - parser.add_argument("-s", "--set", help="Version") |
171 |
| - parser.add_argument("-o", "--show", help="Version", action="store_true") |
172 |
| - parser.add_argument("-m", "--prerelease", help="Set pre-prelease string.") |
173 |
| - return parser.parse_args() |
| 176 | + parser = argparse.ArgumentParser(description="Change versions of cargo projects.") |
| 177 | + parser.add_argument("-p", "--project", help="Project folder", default=".") |
| 178 | + parser.add_argument("-r", "--release", help="Version", action="store_true") |
| 179 | + parser.add_argument( |
| 180 | + "-n", "--next", help="Version", choices=["major", "minor", "patch"] |
| 181 | + ) |
| 182 | + parser.add_argument("-s", "--set", help="Version") |
| 183 | + parser.add_argument("-o", "--show", help="Version", action="store_true") |
| 184 | + parser.add_argument("-m", "--prerelease", help="Set pre-prelease string.") |
| 185 | + return parser.parse_args() |
174 | 186 |
|
175 | 187 |
|
176 | 188 | if __name__ == "__main__":
|
177 |
| - args = parse_args() |
178 |
| - |
179 |
| - old = load(args.project.rstrip('/')) |
180 |
| - |
181 |
| - if args.release: |
182 |
| - new = old.finalize_version() |
183 |
| - new.save(old) |
184 |
| - elif args.next: |
185 |
| - new = old.bump_version(args.next).bump_version("prerelease") |
186 |
| - new.save(old) |
187 |
| - elif args.set: |
188 |
| - # sanity check |
189 |
| - semver.VersionInfo.parse(args.set) |
190 |
| - new = old.set_version(args.set) |
191 |
| - new.save(old) |
192 |
| - elif args.prerelease: |
193 |
| - new = old.set_prerelease(args.prerelease) |
194 |
| - new.save(old) |
195 |
| - elif args.show: |
196 |
| - print(old.show_version()) |
| 189 | + args = parse_args() |
| 190 | + old = load(args.project.rstrip("/")) |
| 191 | + if args.release: |
| 192 | + new = old.finalize_version() |
| 193 | + new.save(old) |
| 194 | + elif args.next: |
| 195 | + new = old.bump_version(args.next).bump_version("prerelease") |
| 196 | + new.save(old) |
| 197 | + elif args.set: |
| 198 | + # sanity check |
| 199 | + semver.VersionInfo.parse(args.set) |
| 200 | + new = old.set_version(args.set) |
| 201 | + new.save(old) |
| 202 | + elif args.prerelease: |
| 203 | + new = old.set_prerelease(args.prerelease) |
| 204 | + new.save(old) |
| 205 | + elif args.show: |
| 206 | + print(old.show_version()) |
0 commit comments