-
-
Notifications
You must be signed in to change notification settings - Fork 93
Don't abort when repos.json
and repos_filtered.json
are not present
#376
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
base: main
Are you sure you want to change the base?
Conversation
These files are not present if `all-repos` exits unexpectedly, e.g. due to SIGINT. Then, the error message: output_dir should only contain repos.json, repos_filtered.json, and directories is shown, as these two files are missing. If they're recreated manually as empty, the error is gone and `all-repos-clone` works again.
all_repos/config.py
Outdated
if not ( | ||
contents >= REPOS_JSON_FILES and | ||
all( | ||
os.path.isdir(os.path.join(output_dir, d)) |
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.
yeah this is definitely not correct -- this breaks the safeguard that's in place
a better fix would be to avoid the situation which breaks the invariant (perhaps in all-repos-clone
?)
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.
Got it. Now I back up both files when all-repos-clone is running and restore them if anything goes wrong (and a newer version of a file wasn't created).
I also added status messages of what's going on. What do you think?
…ot present" This reverts commit 662aaf9.
Top level try..except was added, so both files can be restored when all-repos-clone encounters any issues. Files are restored only if new version hasn't appeared.
probably best to write this as a context manager which only does something on error also on error the state is ~sort of unknown so it is probably best to write empty json files rather than restoring the previous ones (as they may refer to removed repositories) as CI indicates you'll also need to write some tests for this behaviour |
Done. What do you think? |
all_repos/clone.py
Outdated
@contextlib.contextmanager | ||
def safe_write_json(paths: tuple[str, str]) -> Generator[None]: | ||
try: | ||
yield | ||
except KeyboardInterrupt: | ||
print('Aborting...') | ||
for path in paths: | ||
with open(path, 'w') as f: | ||
f.write('{}') | ||
except Exception: | ||
for path in paths: | ||
with open(path, 'w') as f: | ||
f.write('{}') | ||
raise | ||
|
||
|
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 word "safe" usually isn't a good name for a function (often that's exactly where to look for unsafe things) -- it should probably say something more specifically about what it does (specifically it restores blank files on failure)
I think you can also combine the two except
blocks by using except BaseException
(this will include SystemExit
/ KeyboardInterrupt
/ other crashes)
I also had another idea while thinking about this
what instead of attempting to restore these files, all-repos
instead wrote a little marker file that's just always there? that would make this check a lot more consistent (does .all-repos
exist? cool we're good to go!) but we'd also need to think about how to upgrade an existing setup to have that file (perhaps by using the same logic that's already there?)
what do you think of this other idea?
sorry again for taking so long on this. I have had zero free time recently and been back and forth across the country travelling :(
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.
what do you think of this other idea?
Sounds cool, but let's make sure we're on the same page ;)
we'd also need to think about how to upgrade an existing setup to have that file (perhaps by using the same logic that's already there?)
By "the same logic", you mean _check_output_dir
?
Lines 50 to 66 in ee3e0a0
def _check_output_dir(output_dir: str) -> None: | |
if os.path.exists(output_dir): | |
contents = set(os.listdir(output_dir)) | |
if not contents: # empty dir is ok! | |
return | |
if not ( | |
contents >= REPOS_JSON_FILES and | |
all( | |
os.path.isdir(os.path.join(output_dir, d)) | |
for d in contents - REPOS_JSON_FILES | |
) | |
): | |
raise SystemExit( | |
'output_dir should only contain repos.json, ' | |
'repos_filtered.json, and directories', | |
) |
So the new flow in _check_output_dir
would be:
- 1st check: "Empty dir is ok"
- 2nd check: Check only for
.all-repos
file, if it's present we're good to go - 3rd check: (upgrade existing setup) use the current JSON files check from
_check_output_dir
, and create.all-repos
file if other files are there - Fail otherwise
Do I get it right?
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.
yeah I think that's right
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.
Done, now it's a lot simpler :)
Coverage is at 100% w/o new tests, but do you think we need anything additional here?
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 I update readme as well? I see the directory structure is listed there. E.g.
Lines 204 to 210 in ee3e0a0
output/ | |
+--- repos.json | |
+--- repos_filtered.json | |
+--- {repo_key1}/ | |
+--- {repo_key2}/ | |
+--- {repo_key3}/ | |
``` |
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.
of course there should be a test -- otherwise how do we know it works and does what you intend to fix!
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.
Hard to disagree ;)
I added two tests: one checking for the marker file, and the other checking if it's recreated after prior removal. This simulates the upgrade behavior for existing setups
@@ -138,6 +138,7 @@ def main(argv: Sequence[str] | None = None) -> int: | |||
f.write(json.dumps(repos)) | |||
with open(config.repos_filtered_path, 'w') as f: | |||
f.write(json.dumps(repos_filtered)) | |||
open(os.path.join(config.output_dir, '.all-repos'), 'w').close() |
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.
why is this here and the other place? surely it should be one or the other right?
These files are not present if
all-repos
exits unexpectedly, e.g. due to SIGINT.Then, the error message:
is shown, as these two files are missing.
If they're recreated manually as empty, the error is gone and
all-repos-clone
works again, so the current check can be less strict.