-
Notifications
You must be signed in to change notification settings - Fork 135
Add scripts to automate dependencies #2313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
karkhaz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from all the specific comments:
- Please run these files through a linter like pylint or pep-8
- Would you be able to write a top-level script that runs all of these? It's not clear to me how they should be run, or on what order. Some of my comments suggest that the small
update-$TOOL.pyduplicate a lot of code, I honestly think this whole PR could fit in a single file - I think we should avoid duplicating data as much as possible, in particular the names of dependencies that are written elsewhere in the source tree. Ideally this code should be iterating over a JSON file or other file that contains the list of dependencies. The tool names should not be hardcoded anywhere in this PR. I'm working on making that possible here
| @@ -0,0 +1,12 @@ | |||
| #!/usr/bin/python3 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that it makes sense to have three files for CBMC, Viewer, and Kissat, when the files are almost identical.
I like that you've specified the dependencies in a data structure in dependency_links.py. My suggestion is to have a single script that iterates over that dict, instead of manually specifying the key each time.
for dep_name, info in dependency_links.items():
updater = VersionUpdater(kani_dependencies_path, dependencies_links[dep_name])
updater.run_process()This way, we can add more dependencies simply by adding to the dict, without adding any new code, because the logic is the same.
(info is unused in the code above but I think it should be passed to run_process or to the VersionUpdater constructor)
| print(f"Error updating kani version to '{version_number}' in '{file_path}'.") | ||
|
|
||
|
|
||
| def main(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file doesn't appear to do anything because it doesn't actually call update_version
|
|
||
| def main(): | ||
| if len(sys.argv) != 2: | ||
| print("Usage: python update_versions.py <new_version>") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest that instead of asking the user to specify the new version, you should read the version from Cargo.toml, reset the patch number and bump the minor number, and use that. Having to specify a version number manually should be optional.
| @@ -0,0 +1,45 @@ | |||
| #!/usr/bin/python3 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The correct shebang line for python (and most other interpreters) is #!/usr/bin/env python3
| toml.dump(data, f) | ||
|
|
||
| print(f"Version updated succesfully to '{version_number}' in '{file_path}'.") | ||
| except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a need to catch Exception here or anywhere else... I suggest removing the try-catch unless there's a specific exception that's likely to be thrown here.
| else: | ||
| return re.search(r"v?(\d+\.\d+(\.\d+)?(-\S+)?)", response.url).group(1) | ||
|
|
||
| def read_dependencies(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this method need to exist? It seems that its reading the current version numbers. But those never get used, they immediately get overwritten by the latest version number in update_dependencies?
| f.write(line) | ||
|
|
||
|
|
||
| def run_process(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me that you need to walk through the file, updating the existing values. It seems much quicker to overwrite the entire file.
If you have a dict like this:
new_versions = {
"CBMC_VIEWER": 3.9,
"CBMC": 5.99.0,
...
}
then I think it would be much clearer to do this:
with open("kani-dependencies", "w") as handle:
for tool, version in new_versions.items():
print(f"{tool}={version}", file=handle)
then you don't need the if-statement on line 27, which hard-codes the tool names and will thus need to be updated whenever we add a new dependency.
so my suggestion is:
- Get the list of existing tools by opening
kani-dependencies, read each line, split on=, return the first part - Turn that list of tools into the
new versionsdict, using a variation of yourget_latest_versionmethod - Write the dict out using the 3 lines of code above, no need to update the existing file, just overwrite it.
I think this would make the code a lot shorter and clearer, and avoid hardcoding the tool names here. kani-dependencies is the only place those names should be written.
| @@ -0,0 +1,23 @@ | |||
| #!/usr/bin/python3 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to propose that we don't duplicate this information, see this
| else: | ||
| print(f"Command '{command}' executed succesfully, in '{working_directory}'.") | ||
| print(stdout) | ||
| except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure that there's an need for try-except here. If Popen raises, I think the program should crash out early.
There's also no need to use shell=True here, please pass the command as a list.
More generally I'm not sure that there's any need to buffer the output and then immediately print it. I think this code could be a lot shorter:
cmds = [
["cargo", "install", "cargo-outdated"],
["cargo", "outdated", "--workspace"],
]
exit_code = 0
for cmd in cmds:
proc = subprocess.run(cmd, cwd=working_directory)
exit_code += proc.returncode
sys.exit(exit_code)| from dependency_updater import VersionUpdater | ||
|
|
||
| # Set the name of the dependency you want to update | ||
| kani_dependencies_path = "/Users/jaisnan/kani/kani-dependencies" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be using the full path, just kani-dependencies should be fine
|
Another option to consider for updating Rust dependencies is dependabot. It automatically opens a PR when a dependency has a new version. For example, s2n-quic uses it, and yesterday it opened a PR to upgrade Kani to 0.24. This is an example of how it can be integrated: #1909 The annoying thing at the moment is that it would open one PR for every version update as opposed to batching all of them together. There's an open feature request to add support for grouped updates. |
|
|
||
| def main(): | ||
|
|
||
| target_directory = "kani" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be working_directory?
I personally think dependabot is a good candidate for this job, it seems like good practice. I could look into how to use it for Kani. |
Description of changes:
These scripts help automate part of the dependency scripts. While initially tested for manual use, it can be developed and used as part of the CD.
Call-outs:
These scripts only automate the part of the deployment that deal with updating dependencies and updating version numbers. If approved, the rest of the deployment can be added easily (the PR for that is ready to be added).
Testing:
How is this change tested? Manually
Is this a refactor change? No
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.