Skip to content
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

chore: script to automate updates of compatibility test configs for new releases #3541

Merged
merged 14 commits into from
Jun 20, 2023

Conversation

charleenfei
Copy link
Contributor

Description

Currently, we have had to manually replace release versions with new ones if there is EOL or a security issue with one of the versions, and new releases necessitated the manual insertion of the release version into each compatibility text matrix config file.

This script automates each one of these paths depending on user input. It also creates a backup directory before any of the automations are run in case of something going wrong.

Commit Message / Changelog Entry

chore: script to automate updates of compatibility test configs for new releases

see the guidelines for commit messages. (view raw markdown for examples)


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against correct branch (see CONTRIBUTING.md).
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards and Go style guide.
  • Wrote unit and integration tests.
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/).
  • Added relevant godoc comments.
  • Provide a commit message to be used for the changelog entry in the PR description for review.
  • Re-reviewed Files changed in the Github PR explorer.
  • Review Codecov Report in the comment section below once CI passes.

Copy link
Contributor

@colin-axner colin-axner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM, nice work! I'm not so familiar with bash syntax so I just looked at it from a high level. This seems like a nice step in the right direction for automating maintenance of our compatibility tests

This tool should speed up adding/remove versions and reduce likelyhood of missing a file, but it'd still be nice to eventually autogenerate the matrix from a based on a base set of information. Just throwing the idea out there that a file like transfer-chain-a.json should be the same for all release branch directories with the only difference being that older release branches don't test against newer version. This might be a good place to automate test generation next 🤔

cp -R "$directory_path"/* "$backup_dir"

# Step 1: Replace the release version in JSON files
echo "Enter the release version to replace (ie: v4.4.0, leave empty to skip):"
Copy link
Contributor

@colin-axner colin-axner May 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When do we do this?

Edit: I guess if we have a security issue, but then we'd need to have only a single living release branch for that major version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, if we have security issue. but i guess actually for this situation, the delete script is probably better than a replace script

done
fi

# Step 2: Add new release version to testing configuration files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a remove script as well?

@charleenfei charleenfei marked this pull request as draft May 23, 2023 14:07
@DimitrisJim DimitrisJim force-pushed the charly/release_cutting_script branch 3 times, most recently from 1263b1b to 365a24f Compare June 5, 2023 22:17
@@ -0,0 +1,101 @@
#!/usr/bin/python3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an alternative in Python. Though bash does the trick, the script seemed like it was doing enough to warrant a language that doesn't make your eyes hurt.

I'm generally open to sticking with bash but find this solution both more readable, more maintainable and less error prone.

Still need to get some feedback from Carlos to make sure the script does what is typically required. Other than that, willing to drop this and go with bash if people prefer that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely like the idea of this sort of tool existing as a python script instead of a bash one. It would be far easier to maintain.

@DimitrisJim DimitrisJim force-pushed the charly/release_cutting_script branch 4 times, most recently from 9211f01 to 0296228 Compare June 5, 2023 22:40
Copy link
Contributor

@chatton chatton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing the python version, I really like the look of this script being in python. I think it would be a lot more maintainable (and even testable if we wanted to go to those lengths)

If we do go forward with the python version, I'd like to see type hints introduced since we'd be starting with a very small amount of code it would be easy to do.

@@ -0,0 +1,101 @@
#!/usr/bin/python3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely like the idea of this sort of tool existing as a python script instead of a bash one. It would be far easier to maintain.

def find_json_files(directory, ignores=(".",)):
""" Find JSON files in a directory. By default, ignore hidden directories."""
for root, dirs, files in os.walk(directory):
dirs[:] = (d for d in dirs if not d.startswith(ignores))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not used to this syntax, my guess is that the [:]' is creating a copy or new variable so as to not re-assign the variable dirs` in the for loop. I think this is a little awkward and would be easier to understand if we just used a different variable

i.e.

filtered_dirs = (d for d in dirs if not d.startswith(ignores))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally ugly, agree there but this in-place assignment of the returned directories allows for skipping any filtered entries when topdown=True. Alternative would be an iteration over the dirs and manual del of the correct indices.

In general, ignores was added based on the initial bash script considering a .backup dir being present. Since I haven't added that in (thinking a git restore could do the trick) this could also be removed).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see so we're actually modifying the dirs that we're already iterating over

dirs[:] = (d for d in dirs if not d.startswith(ignores))
for file_ in files:
if file_.endswith(".json"):
yield os.path.join(root, file_)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love the generator usage 👍


def has_release_version(json_file, keys, version):
""" Check if the json file has the version in question. """
return any(version in row for row in (json_file[key] for key in keys))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just a personal preference, but I like keeping list comprehensions as shallow as possible. I find it can be difficult to read if you're not very experienced with python.

What about just

def has_release_version(json_file, keys, version):
    """ Check if the json file has the version in question. """
    values = (json_file[key] for key in keys)
    return any(version in row for row in values)

the flattening in the any(version in row for row in values) is not necessarily intuitive at first.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but don't you know that one-liners always win? Will split up, serves no real point.

Copy link
Contributor

@chatton chatton Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one liners win until someone else needs to modify it 😄

for row in (json_file[key] for key in keys):
if recent not in row:
continue
if op == "add":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe these can be constants? OP_ADD, OP_REMOVE etc.

""" Check if the json file has the version in question. """
return any(version in row for row in (json_file[key] for key in keys))

def update_version(json_file, keys, args):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically handled in the parse args, code, but maybe we can raise a ValueError if the operation is unknown in this function too just to be safe and explode when bad inputs are used.

require_version(args)
return args

def main(args):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing I noticed was that when we run the script giving an version that doesn't exists, it executes successfully but is just silent. I think it would be no harm either logging that the version wasn't found, or raise a ValueError with the input not having been found.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, can see the value in that

# Toggle if required. Indent = 4 matches jq.
DUMP_ARGS = {"indent": 4, "sort_keys": False, "ensure_ascii": False}
DUMP_ARGS: Dict[Any, Any] = {
Copy link
Contributor

@DimitrisJim DimitrisJim Jun 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunate but mypy (the type checker for the uninitiated) throws a big hissy-fit here. Typing **kwargs in general are a pain iirc.

charleenfei and others added 6 commits June 13, 2023 10:15
 - Make script executable
 - Search in $PATH for bash
 - Exit on uninitialized vars, non-zero exit status in pipelines
 - Order the releases
 - Pretty print output.
@DimitrisJim DimitrisJim force-pushed the charly/release_cutting_script branch from 5eb664e to 9a0f611 Compare June 13, 2023 07:37
@DimitrisJim DimitrisJim marked this pull request as ready for review June 13, 2023 09:16
@DimitrisJim DimitrisJim force-pushed the charly/release_cutting_script branch from 1310304 to 9708c41 Compare June 13, 2023 09:25
@DimitrisJim DimitrisJim force-pushed the charly/release_cutting_script branch from 9708c41 to 52639b2 Compare June 13, 2023 09:30
Copy link
Contributor

@chatton chatton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thank you so much @charleenfei & @DimitrisJim , this will make the release process a lot simpler 🥇

One final nit, compat.py is a bit ambiguous, maybe we could name the file update_compatibility_tests.py to be more explicit, not a blocker at all though.

# Suggestions for recent and new versions.
SUGGESTION: str = "for example, v4.3.0 or 4.3.0"
# Supported Operations.
Operation = enum.Enum("Operation", ["ADD", "REMOVE", "REPLACE"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️


def sorter(key: str) -> str:
"""Since 'main' < 'vX.X.X' and we want to have 'main' as the first entry
in the list, we return a version that is considerably large. If ibc-go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

Copy link
Contributor Author

@charleenfei charleenfei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tysm for making a much more readable/maintainable version of my hack <3

Copy link
Contributor

@damiannolan damiannolan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome stuff! The python code looks pretty clean, its been a while since I looked at any! I think its more maintainable than bash.

LGTM 🚀

def sorter(key: str) -> str:
"""Since 'main' < 'vX.X.X' and we want to have 'main' as the first entry
in the list, we return a version that is considerably large. If ibc-go
reaches this version I'll wear my dunce hat and go sit in the corner.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will also eat my shorts on live stream

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At our current pace of 3 major releases a year we can get there in 33330 years. Think about the poor person on whose face this time bomb will explode, man.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a quantum commit in the year 35353 "F*** you, DimitrisJim."

Copy link
Contributor

@crodriguezvega crodriguezvega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work, @DimitrisJim! Great idea and initial implementation, @charleenfei!

def sorter(key: str) -> str:
"""Since 'main' < 'vX.X.X' and we want to have 'main' as the first entry
in the list, we return a version that is considerably large. If ibc-go
reaches this version I'll wear my dunce hat and go sit in the corner.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At our current pace of 3 major releases a year we can get there in 33330 years. Think about the poor person on whose face this time bomb will explode, man.

@DimitrisJim DimitrisJim merged commit 2e44243 into main Jun 20, 2023
@DimitrisJim DimitrisJim deleted the charly/release_cutting_script branch June 20, 2023 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants